From 4727038923b1b19f6bc459974e4418572d3b0d40 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Tue, 22 Aug 2023 22:02:21 +0530 Subject: [PATCH 1/4] [Draft]Implement egress packet dropping Signed-off-by: Ashish Tiwari --- Dockerfile | 5 +- Makefile | 2 +- bpf/daemon.c | 144 ++++++++++++++++++++++++++++++++++++---- pkg/monitor/monitor.go | 56 +++++++++++++--- pkg/types/filterRule.go | 1 + pkg/types/packet.go | 1 + sample/dropit.yaml | 4 +- 7 files changed, 185 insertions(+), 28 deletions(-) diff --git a/Dockerfile b/Dockerfile index e94b510..49c8386 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM golang:1.20 as build -RUN apt update -y; apt install -y build-essential clang libbpf-dev bpftool - +RUN apt update -y; apt install -y build-essential clang libbpf-dev bpftool linux-headers-generic +RUN ln -s /usr/include/x86_64-linux-gnu/asm /usr/include/asm WORKDIR /build ADD . . RUN make build @@ -11,7 +11,6 @@ FROM debian:stable #RUN apt update -y; apt install -y apache2 RUN apt update -y; apt install -y inotify-tools - WORKDIR /dropit COPY scripts/entrypoint.sh /dropit/entrypoint.sh COPY --from=build /build/bpf/vmlinux.h /dropit/vmlinux.h diff --git a/Makefile b/Makefile index dd42615..aa9ac4b 100644 --- a/Makefile +++ b/Makefile @@ -30,4 +30,4 @@ build: generate generate: bpftool btf dump file /sys/kernel/btf/vmlinux format c > $(BPF_DIR)/vmlinux.h - clang -g -O2 -c -target bpf -o $(BPF_DIR)/daemon.o $(BPF_DIR)/daemon.c + clang -g -O2 -I/usr/include/linux -c -target bpf -o $(BPF_DIR)/daemon.o $(BPF_DIR)/daemon.c diff --git a/bpf/daemon.c b/bpf/daemon.c index 322c49a..afbb586 100644 --- a/bpf/daemon.c +++ b/bpf/daemon.c @@ -1,10 +1,51 @@ //+build ignore -#include "vmlinux.h" +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +typedef __u8 u8; + +typedef __s16 s16; + +typedef __u16 u16; + +typedef __s32 s32; + +typedef __u32 u32; + +typedef __s64 s64; + +typedef __u64 u64; + +typedef _Bool bool; #define MATCH_ALL 0 +// These macros represent the direction of traffic in a packet and in a filter rule(to check when to evaluate it). +// Direction will be represented by u8 +#define Ingress 0 +#define Egress 1 + + +// These offsets are defined as per the __sk_buf struct of a particular version. Might break?? +#define OFFSET_PROTOCOL 4 +#define OFFSET_REMOTE_IP 19 +#define OFFSET_LOCAL_IP 20 +#define OFFSET_REMOTE_PORT 23 +#define OFFSET_LOCAL_PORT 24 + + + + extern int LINUX_KERNEL_VERSION __kconfig; struct packet { @@ -15,6 +56,7 @@ struct packet { u16 dest_port; u8 protocol; bool is_dropped; + u8 direction; }; struct filter_rule { @@ -22,6 +64,7 @@ struct filter_rule { u16 source_port; u16 dest_port; u8 protocol; + u8 direction; }; struct { @@ -38,10 +81,12 @@ struct { __uint(max_entries, NO_OF_RULES); // 256 or 2^8 filter rules } filter_rules SEC(".maps"); +// The same lookup_ctx will be used for both XDP and tc related filtering. Different fields will be set depending on filter_rule->direction. struct lookup_ctx { struct packet *pk; struct filter_rule *fr; - int output; + int xdp_output; + int tc_output; }; static void push_log(struct packet *pk) { @@ -72,19 +117,94 @@ static u64 filter_packet(struct bpf_map *map, u32 *key, pk->source_ip, pk->source_port, pk->dest_port, pk->protocol); */ - if( (value->source_ip == MATCH_ALL || value->source_ip == pk->source_ip) && // match source IP - (value->source_port == MATCH_ALL || value->source_port == pk->source_port) && //match source port - (value->dest_port == MATCH_ALL || value->dest_port == pk->dest_port) && // match destination port - (value->protocol == MATCH_ALL || value->protocol == pk->protocol)){ // match protocol - ctx->output = XDP_DROP; + if((value->direction == Ingress) && // Use XDP filtering on Ingress + (value->source_ip == MATCH_ALL || value->source_ip == pk->source_ip) && // match source IP + (value->source_port == MATCH_ALL || value->source_port == pk->source_port) && //match source port + (value->dest_port == MATCH_ALL || value->dest_port == pk->dest_port) && // match destination port + (value->protocol == MATCH_ALL || value->protocol == pk->protocol)){ // match protocol + ctx->xdp_output = XDP_DROP; ctx->fr = value; return 1; - } + } + + if((value->direction == Egress) && // Use tc filtering on Egress + (value->source_ip == MATCH_ALL || value->source_ip == pk->source_ip) && // match source IP + (value->source_port == MATCH_ALL || value->source_port == pk->source_port) && //match source port + (value->dest_port == MATCH_ALL || value->dest_port == pk->dest_port) && // match destination port + (value->protocol == MATCH_ALL || value->protocol == pk->protocol)){ // match protocol + ctx->tc_output = TC_ACT_SHOT; + ctx->fr = value; + return 1; + } return 0; } + +SEC("tc") +int intercept_packets_tc_egress(struct __sk_buff *skb){ + //Parse skbuff to create the packet struct and pass that to filter_packets + struct packet pk; + pk.direction = Egress; + + + + //create a copy of skbuff locally + // currently failing verification + // struct __sk_buff localskb; + // int status = bpf_skb_load_bytes(skb,0,&localskb,sizeof(struct __sk_buff)); + // if (!status){ + // return TC_ACT_SHOT; //for testing + + // } + + // pk.dest_ip = localskb.remote_ip4; + // pk.dest_port = localskb.remote_port; + // pk.source_ip = localskb.local_ip4; + // pk.source_port = localskb.local_port; + // pk.protocol = localskb.protocol; + + + //individually filling + // bpf_skb_load_bytes(skb,sizeof(__u32)*OFFSET_REMOTE_IP,&pk.dest_ip,sizeof(__u32)); + // bpf_skb_load_bytes(skb,sizeof(__u32)*OFFSET_LOCAL_IP,&pk.source_ip,sizeof(__u32)); + // bpf_skb_load_bytes(skb,sizeof(__u32)*OFFSET_LOCAL_PORT,&pk.source_port,sizeof(__u32)); + // bpf_skb_load_bytes(skb,sizeof(__u32)*OFFSET_REMOTE_PORT,&pk.dest_port,sizeof(__u32)); + // bpf_skb_load_bytes(skb,sizeof(__u32)*OFFSET_PROTOCOL,&pk.protocol,sizeof(__u32)); + + //Direct packet access + // pk.dest_ip = skb->remote_ip4; + // pk.dest_port = skb->remote_port; + // pk.source_ip = skb->local_ip4; + // pk.source_port = skb->local_port; + // pk.protocol = skb->protocol; + + + struct lookup_ctx data = { + .pk = &pk, + .tc_output = 0, + }; + bpf_for_each_map_elem(&filter_rules, filter_packet, &data, 0); + + if(data.tc_output == TC_ACT_SHOT){ + pk.is_dropped = 1; + struct filter_rule *fr = data.fr; + // network security event logs only work on kernel 5.16 +#if LINUX_KERNEL_VERSION >= KERNEL_VERSION(5, 16, 0) + bpf_printk("Rule: %u %u %u %u. Packet %u %u %u %u", fr->source_ip, + fr->source_port, fr->dest_port, fr->protocol, pk.source_ip, + pk.source_port, pk.dest_port, pk.protocol); +#endif + push_log(&pk); + return TC_ACT_SHOT; + + } + push_log(&pk); + return TC_ACT_OK; +} + + SEC("xdp") -int intercept_packets(struct xdp_md *ctx) { +int intercept_packets_xdp_ingress(struct xdp_md *ctx) { // We mark the start and end of our ethernet frame void *ethernet_start = (void *)(long)ctx->data; void *ethernet_end = (void *)(long)ctx->data_end; @@ -106,7 +226,7 @@ int intercept_packets(struct xdp_md *ctx) { pk.size = (ethernet_end - ethernet_start); pk.dest_port = pk.source_port = 0; pk.is_dropped = 0; - + pk.direction = Ingress; // check the protocol and get port if (pk.protocol == IPPROTO_TCP) { struct tcphdr *tcp = (void *)ip_packet + sizeof(*ip_packet); @@ -128,12 +248,12 @@ int intercept_packets(struct xdp_md *ctx) { struct lookup_ctx data = { .pk = &pk, - .output = 0, + .xdp_output = 0, }; bpf_for_each_map_elem(&filter_rules, filter_packet, &data, 0); - if (data.output == XDP_DROP) { + if (data.xdp_output == XDP_DROP) { pk.is_dropped = 1; struct filter_rule *fr = data.fr; // network security event logs only work on kernel 5.16 diff --git a/pkg/monitor/monitor.go b/pkg/monitor/monitor.go index 2e1392b..3abc04a 100644 --- a/pkg/monitor/monitor.go +++ b/pkg/monitor/monitor.go @@ -18,6 +18,11 @@ import ( "github.com/spf13/viper" ) +// TODO: Allow passing these from env variable +var XDP_PROG_NAME = "intercept_packets_xdp_ingress" +var TC_PROG_NAME = "intercept_packets_tc_egress" +var BPF_ELF_NAME = "daemon.o" + type Monitor struct { // userspace filter map FilterRuleBytesMap map[string]types.FilterRuleBytes @@ -50,7 +55,7 @@ func (m *Monitor) StartMonitor(interfaceName, cfgPath *string) error { } } - bpfModule, err := bpf.NewModuleFromFile("daemon.o") + bpfModule, err := bpf.NewModuleFromFile(BPF_ELF_NAME) if err != nil { return err } @@ -117,19 +122,49 @@ func (m *Monitor) InitBPF(interfaceName string, bpfModule *bpf.Module) error { return err } - xdpProg, err := bpfModule.GetProgram("intercept_packets") - if xdpProg == nil { - if err != nil { - return fmt.Errorf("Failed to get xdp program %s", err) + //Disabled for testing... + //XDP initialiasation + // xdpProg, err := bpfModule.GetProgram(XDP_PROG_NAME) + // if xdpProg == nil { + // if err != nil { + // return fmt.Errorf("Failed to get xdp program %s", err) + // } + // return fmt.Errorf("Empty xdp program loaded") + // } + + // _, err = xdpProg.AttachXDP(interfaceName) + // if err != nil { + // return err + // } + + //TC initialisation + hook := bpfModule.TcHookInit() + err = hook.SetInterfaceByName(interfaceName) + if err != nil { + return fmt.Errorf("failed to set tc hook on interface %s: %v", interfaceName, err) + } + + hook.SetAttachPoint(bpf.BPFTcEgress) + err = hook.Create() + if err != nil { + if errno, ok := err.(syscall.Errno); ok && errno != syscall.EEXIST { + return fmt.Errorf("failed to create tc hook on interface %s: %v", interfaceName, err) } - return fmt.Errorf("Empty xdp program loaded") } - _, err = xdpProg.AttachXDP(interfaceName) + tcProg, err := bpfModule.GetProgram(TC_PROG_NAME) + if tcProg == nil { + return fmt.Errorf("could not find program %s: ", TC_PROG_NAME) + } + + var tcOpts bpf.TcOpts + tcOpts.ProgFd = int(tcProg.FileDescriptor()) + err = hook.Attach(&tcOpts) if err != nil { return err } + // Initialising Map m.FilterMap, err = bpfModule.GetMap("filter_rules") if err != nil { return err @@ -146,8 +181,8 @@ func (m *Monitor) StartLogging(eventsChan chan ([]byte), ringbuf *bpf.RingBuffer packetIdx := 0 log.Printf( - "|%-5s|%-15s|%-8s|%-15s|%-8s|%-5s|%-10s|%-8s|\n", - "No.", "Src IP", "Src Port", "Dest IP", "Dst Port", "Proto", "Size", "Status", + "|%-5s|%-15s|%-8s|%-15s|%-8s|%-5s|%-10s|%-8s|%-15s|\n", + "No.", "Src IP", "Src Port", "Dest IP", "Dst Port", "Proto", "Size", "Status", "Direction", ) for { @@ -157,7 +192,7 @@ func (m *Monitor) StartLogging(eventsChan chan ([]byte), ringbuf *bpf.RingBuffer pk := parseByteData(eventBytes) log.Printf( - "|%-5d|%-15s|%-8d|%-15s|%-8d|%-5s|%-10d|%-8s|\n", + "|%-5d|%-15s|%-8d|%-15s|%-8d|%-5s|%-10d|%-8s|%-15s|\n", packetIdx, pk.SourceIP, pk.SourcePort, @@ -166,6 +201,7 @@ func (m *Monitor) StartLogging(eventsChan chan ([]byte), ringbuf *bpf.RingBuffer pk.Protocol, pk.Size, pk.Status, + pk.Direction, ) case sig := <-m.SigChan: diff --git a/pkg/types/filterRule.go b/pkg/types/filterRule.go index efae2b6..fa85228 100644 --- a/pkg/types/filterRule.go +++ b/pkg/types/filterRule.go @@ -5,4 +5,5 @@ type FilterRuleBytes struct { SourcePort uint16 DestinationPort uint16 Protocol uint8 + Direction uint8 } diff --git a/pkg/types/packet.go b/pkg/types/packet.go index 92fd7d2..c28a176 100644 --- a/pkg/types/packet.go +++ b/pkg/types/packet.go @@ -8,4 +8,5 @@ type Packet struct { DestPort uint16 Protocol string Status string + Direction string } diff --git a/sample/dropit.yaml b/sample/dropit.yaml index 7921ebb..b4bd802 100644 --- a/sample/dropit.yaml +++ b/sample/dropit.yaml @@ -1,6 +1,6 @@ rules: - id: "rule1" sourceIP: '*' - destinationPort: 8080 - sourcePort: '*' + destinationPort: '*' + sourcePort: 8080 protocol: '*' From d7bf2b93ab2e67909a8ef296b6293aba0300e865 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Tue, 12 Sep 2023 01:32:49 +0530 Subject: [PATCH 2/4] tc egress working --- Dockerfile | 2 +- bpf/daemon.c | 167 ++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 141 insertions(+), 28 deletions(-) diff --git a/Dockerfile b/Dockerfile index 49c8386..e6cef85 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM golang:1.20 as build -RUN apt update -y; apt install -y build-essential clang libbpf-dev bpftool linux-headers-generic +RUN apt update -y; apt install -y build-essential clang libbpf-dev bpftool linux-headers-generic gcc-multilib RUN ln -s /usr/include/x86_64-linux-gnu/asm /usr/include/asm WORKDIR /build ADD . . diff --git a/bpf/daemon.c b/bpf/daemon.c index afbb586..00804b4 100644 --- a/bpf/daemon.c +++ b/bpf/daemon.c @@ -1,17 +1,25 @@ //+build ignore - +#include #include #include #include #include #include #include +#include #include #include -#include #include +#include #include #include +#include +#include +/* #include */ + +typedef __kernel_size_t size_t; + +typedef __kernel_ssize_t ssize_t; typedef __u8 u8; @@ -47,7 +55,12 @@ typedef _Bool bool; extern int LINUX_KERNEL_VERSION __kconfig; - +/* copy of 'struct ethhdr' without __packed */ +struct eth_hdr { + unsigned char h_dest[ETH_ALEN]; + unsigned char h_source[ETH_ALEN]; + unsigned short h_proto; +}; struct packet { u32 source_ip; u32 dest_ip; @@ -67,6 +80,37 @@ struct filter_rule { u8 direction; }; +enum { + IPPROTO_IP = 0, + IPPROTO_ICMP = 1, + IPPROTO_IGMP = 2, + IPPROTO_IPIP = 4, + IPPROTO_TCP = 6, + IPPROTO_EGP = 8, + IPPROTO_PUP = 12, + IPPROTO_UDP = 17, + IPPROTO_IDP = 22, + IPPROTO_TP = 29, + IPPROTO_DCCP = 33, + IPPROTO_IPV6 = 41, + IPPROTO_RSVP = 46, + IPPROTO_GRE = 47, + IPPROTO_ESP = 50, + IPPROTO_AH = 51, + IPPROTO_MTP = 92, + IPPROTO_BEETPH = 94, + IPPROTO_ENCAP = 98, + IPPROTO_PIM = 103, + IPPROTO_COMP = 108, + IPPROTO_L2TP = 115, + IPPROTO_SCTP = 132, + IPPROTO_UDPLITE = 136, + IPPROTO_MPLS = 137, + IPPROTO_ETHERNET = 143, + IPPROTO_RAW = 255, + IPPROTO_MPTCP = 262, + IPPROTO_MAX = 263, +}; struct { __uint(type, BPF_MAP_TYPE_RINGBUF); __uint(max_entries, 1 << 24); // 16777216 or 2^24 entries @@ -141,20 +185,67 @@ static u64 filter_packet(struct bpf_map *map, u32 *key, SEC("tc") -int intercept_packets_tc_egress(struct __sk_buff *skb){ +int intercept_packets_tc_egress(struct __sk_buff *ctx){ //Parse skbuff to create the packet struct and pass that to filter_packets struct packet pk; pk.direction = Egress; + // We mark the start and end of our ethernet frame + void *ethernet_start = (void *)(long)ctx->data; + void *ethernet_end = (void *)(long)ctx->data_end; + struct ethhdr *ethernet_frame = ethernet_start; + // Check if we have the entire ethernet frame + if ((void *)ethernet_frame + sizeof(*ethernet_frame) <= ethernet_end) { + struct iphdr *ip_packet = ethernet_start + sizeof(*ethernet_frame); + // Check if the IP packet is within the bounds of ethernet frame + if ((void *)ip_packet + sizeof(*ip_packet) <= ethernet_end) { + // extract info from the IP packet + struct packet pk; + pk.source_ip = ip_packet->saddr; + pk.dest_ip = ip_packet->daddr; + pk.protocol = ip_packet->protocol; + pk.size = (ethernet_end - ethernet_start); + pk.dest_port = pk.source_port = 0; + pk.is_dropped = 0; + } + // check the protocol and get port + if (pk.protocol == IPPROTO_TCP) { + struct tcphdr *tcp = (void *)ip_packet + sizeof(*ip_packet); + if ((void *)tcp + sizeof(*tcp) <= ethernet_end) { + // Checking if the destination port matches with the specified port + pk.source_port = tcp->source; + pk.dest_port = tcp->dest; + } + } + if (pk.protocol == IPPROTO_UDP) { + struct udphdr *udp = (void *)ip_packet + sizeof(*ip_packet); + if ((void *)udp + sizeof(*udp) <= ethernet_end) { + // Checking if the destination port matches with the specified port + pk.source_port = udp->source; + pk.dest_port = udp->dest; + } + } + struct lookup_ctx data = { + .pk = &pk, + .tc_output = 0, + }; + bpf_for_each_map_elem(&filter_rules, filter_packet, &data, 0); + push_log(&pk); + if(data.tc_output == TC_ACT_SHOT){ + return TC_ACT_SHOT; + } + return TC_ACT_OK; + } +//------ + //First Try //create a copy of skbuff locally // currently failing verification // struct __sk_buff localskb; // int status = bpf_skb_load_bytes(skb,0,&localskb,sizeof(struct __sk_buff)); // if (!status){ // return TC_ACT_SHOT; //for testing - // } // pk.dest_ip = localskb.remote_ip4; @@ -164,6 +255,7 @@ int intercept_packets_tc_egress(struct __sk_buff *skb){ // pk.protocol = localskb.protocol; + //Second Try //individually filling // bpf_skb_load_bytes(skb,sizeof(__u32)*OFFSET_REMOTE_IP,&pk.dest_ip,sizeof(__u32)); // bpf_skb_load_bytes(skb,sizeof(__u32)*OFFSET_LOCAL_IP,&pk.source_ip,sizeof(__u32)); @@ -171,35 +263,56 @@ int intercept_packets_tc_egress(struct __sk_buff *skb){ // bpf_skb_load_bytes(skb,sizeof(__u32)*OFFSET_REMOTE_PORT,&pk.dest_port,sizeof(__u32)); // bpf_skb_load_bytes(skb,sizeof(__u32)*OFFSET_PROTOCOL,&pk.protocol,sizeof(__u32)); - //Direct packet access + //Third Try + //Direct packet access doesn't work with socket filter programs smh :( // pk.dest_ip = skb->remote_ip4; - // pk.dest_port = skb->remote_port; + // pk.dest_port = skb->remote_port>>16; // pk.source_ip = skb->local_ip4; - // pk.source_port = skb->local_port; + // pk.source_port = skb->local_port>>16; // pk.protocol = skb->protocol; + // void *pktdata = (void* )(long)skb->data; + // struct eth_hdr* eth = pktdata; + // void *pktdata_end = (void*)(long)skb->data_end; + // int key = 0,*ifindex; + // int ret; - struct lookup_ctx data = { - .pk = &pk, - .tc_output = 0, - }; - bpf_for_each_map_elem(&filter_rules, filter_packet, &data, 0); - - if(data.tc_output == TC_ACT_SHOT){ - pk.is_dropped = 1; - struct filter_rule *fr = data.fr; - // network security event logs only work on kernel 5.16 -#if LINUX_KERNEL_VERSION >= KERNEL_VERSION(5, 16, 0) - bpf_printk("Rule: %u %u %u %u. Packet %u %u %u %u", fr->source_ip, - fr->source_port, fr->dest_port, fr->protocol, pk.source_ip, - pk.source_port, pk.dest_port, pk.protocol); -#endif - push_log(&pk); - return TC_ACT_SHOT; + // if (pktdata+sizeof(*eth)>pktdata_end){ + // push_log(&pk); + // return TC_ACT_SHOT; + // } + // struct iphdr *iph = pktdata+sizeof(*eth); + // pk.protocol = iph->protocol; + // pk.source_ip = iph->saddr; + // pk.dest_ip = iph->daddr; - } + //testing + + + //------------ + + // struct lookup_ctx data = { + // .pk = &pk, + // .tc_output = 0, + // }; + // bpf_for_each_map_elem(&filter_rules, filter_packet, &data, 0); + +// if(data.tc_output == TC_ACT_SHOT){ +// pk.is_dropped = 1; +// struct filter_rule *fr = data.fr; +// // network security event logs only work on kernel 5.16 +// #if LINUX_KERNEL_VERSION >= KERNEL_VERSION(5, 16, 0) +// bpf_printk("Rule: %u %u %u %u. Packet %u %u %u %u", fr->source_ip, +// fr->source_port, fr->dest_port, fr->protocol, pk.source_ip, +// pk.source_port, pk.dest_port, pk.protocol); +// #endif +// push_log(&pk); +// return TC_ACT_SHOT; + +// } + pk.source_ip = 100; push_log(&pk); - return TC_ACT_OK; + return TC_ACT_SHOT; } From 421a03a3e3018f29397043b759e1f1af61134429 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Thu, 21 Sep 2023 01:54:06 +0530 Subject: [PATCH 3/4] cleanup code --- bpf/daemon.c | 78 +++--------------------------------------- pkg/config/config.go | 31 +++++++++++++++-- pkg/monitor/monitor.go | 27 ++++++++------- sample/dropit.yaml | 11 ++++-- 4 files changed, 55 insertions(+), 92 deletions(-) diff --git a/bpf/daemon.c b/bpf/daemon.c index 00804b4..3bab20b 100644 --- a/bpf/daemon.c +++ b/bpf/daemon.c @@ -147,6 +147,7 @@ static void push_log(struct packet *pk) { packet->protocol = pk->protocol; packet->size = pk->size; packet->is_dropped = pk->is_dropped; + packet->direction = pk->direction; bpf_ringbuf_submit(packet, 0); } @@ -232,85 +233,14 @@ int intercept_packets_tc_egress(struct __sk_buff *ctx){ .tc_output = 0, }; bpf_for_each_map_elem(&filter_rules, filter_packet, &data, 0); - push_log(&pk); if(data.tc_output == TC_ACT_SHOT){ + pk.is_dropped = 1; + push_log(&pk); return TC_ACT_SHOT; } + push_log(&pk); return TC_ACT_OK; } -//------ - //First Try - //create a copy of skbuff locally - // currently failing verification - // struct __sk_buff localskb; - // int status = bpf_skb_load_bytes(skb,0,&localskb,sizeof(struct __sk_buff)); - // if (!status){ - // return TC_ACT_SHOT; //for testing - // } - - // pk.dest_ip = localskb.remote_ip4; - // pk.dest_port = localskb.remote_port; - // pk.source_ip = localskb.local_ip4; - // pk.source_port = localskb.local_port; - // pk.protocol = localskb.protocol; - - - //Second Try - //individually filling - // bpf_skb_load_bytes(skb,sizeof(__u32)*OFFSET_REMOTE_IP,&pk.dest_ip,sizeof(__u32)); - // bpf_skb_load_bytes(skb,sizeof(__u32)*OFFSET_LOCAL_IP,&pk.source_ip,sizeof(__u32)); - // bpf_skb_load_bytes(skb,sizeof(__u32)*OFFSET_LOCAL_PORT,&pk.source_port,sizeof(__u32)); - // bpf_skb_load_bytes(skb,sizeof(__u32)*OFFSET_REMOTE_PORT,&pk.dest_port,sizeof(__u32)); - // bpf_skb_load_bytes(skb,sizeof(__u32)*OFFSET_PROTOCOL,&pk.protocol,sizeof(__u32)); - - //Third Try - //Direct packet access doesn't work with socket filter programs smh :( - // pk.dest_ip = skb->remote_ip4; - // pk.dest_port = skb->remote_port>>16; - // pk.source_ip = skb->local_ip4; - // pk.source_port = skb->local_port>>16; - // pk.protocol = skb->protocol; - - // void *pktdata = (void* )(long)skb->data; - // struct eth_hdr* eth = pktdata; - // void *pktdata_end = (void*)(long)skb->data_end; - // int key = 0,*ifindex; - // int ret; - - // if (pktdata+sizeof(*eth)>pktdata_end){ - // push_log(&pk); - // return TC_ACT_SHOT; - // } - // struct iphdr *iph = pktdata+sizeof(*eth); - // pk.protocol = iph->protocol; - // pk.source_ip = iph->saddr; - // pk.dest_ip = iph->daddr; - - //testing - - - //------------ - - // struct lookup_ctx data = { - // .pk = &pk, - // .tc_output = 0, - // }; - // bpf_for_each_map_elem(&filter_rules, filter_packet, &data, 0); - -// if(data.tc_output == TC_ACT_SHOT){ -// pk.is_dropped = 1; -// struct filter_rule *fr = data.fr; -// // network security event logs only work on kernel 5.16 -// #if LINUX_KERNEL_VERSION >= KERNEL_VERSION(5, 16, 0) -// bpf_printk("Rule: %u %u %u %u. Packet %u %u %u %u", fr->source_ip, -// fr->source_port, fr->dest_port, fr->protocol, pk.source_ip, -// pk.source_port, pk.dest_port, pk.protocol); -// #endif -// push_log(&pk); -// return TC_ACT_SHOT; - -// } - pk.source_ip = 100; push_log(&pk); return TC_ACT_SHOT; } diff --git a/pkg/config/config.go b/pkg/config/config.go index aac7e5e..c11fd9b 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -5,12 +5,35 @@ import ( "fmt" "net/netip" "strconv" + "strings" "syscall" "github.com/DelusionalOptimist/dropit/pkg/types" "github.com/spf13/viper" ) +func DirectionStringToInt(direction string) uint8 { + switch strings.ToLower(direction) { + case "ingress": + return 0 + case "egress": + return 1 + default: + return 2 + } +} + +func DirectionIntToString(direction uint8) string { + switch direction { + case 0: + return "ingress" + case 1: + return "egress" + default: + return "unknown" + } +} + type Config struct { Rules []struct { ID string `yaml:"id"` @@ -18,6 +41,7 @@ type Config struct { SourcePort string `yaml:"sourcePort"` DestinationPort string `yaml:"destinationPort"` Protocol string `yaml:"protocol"` + Direction string `yaml:"direction"` } `yaml:"rules"` } @@ -108,10 +132,11 @@ func (cfg *Config) parseConfig() (map[string]types.FilterRuleBytes, error) { } byteRule := types.FilterRuleBytes{ - SourceIP: srcIP, - SourcePort: uint16(srcPort << 8 | srcPort >> 8), - DestinationPort: uint16(destPort << 8 | destPort >> 8), + SourceIP: srcIP, + SourcePort: uint16(srcPort<<8 | srcPort>>8), + DestinationPort: uint16(destPort<<8 | destPort>>8), Protocol: protocol, + Direction: DirectionStringToInt(rule.Direction), } fr[rule.ID] = byteRule diff --git a/pkg/monitor/monitor.go b/pkg/monitor/monitor.go index 3abc04a..eaa9b78 100644 --- a/pkg/monitor/monitor.go +++ b/pkg/monitor/monitor.go @@ -124,18 +124,18 @@ func (m *Monitor) InitBPF(interfaceName string, bpfModule *bpf.Module) error { //Disabled for testing... //XDP initialiasation - // xdpProg, err := bpfModule.GetProgram(XDP_PROG_NAME) - // if xdpProg == nil { - // if err != nil { - // return fmt.Errorf("Failed to get xdp program %s", err) - // } - // return fmt.Errorf("Empty xdp program loaded") - // } - - // _, err = xdpProg.AttachXDP(interfaceName) - // if err != nil { - // return err - // } + xdpProg, err := bpfModule.GetProgram(XDP_PROG_NAME) + if xdpProg == nil { + if err != nil { + return fmt.Errorf("Failed to get xdp program %s", err) + } + return fmt.Errorf("Empty xdp program loaded") + } + + _, err = xdpProg.AttachXDP(interfaceName) + if err != nil { + return err + } //TC initialisation hook := bpfModule.TcHookInit() @@ -339,7 +339,6 @@ func parseByteData(data []byte) types.Packet { DestPort: binary.BigEndian.Uint16(data[14:16]), Protocol: types.GetProtoName(data[16]), Status: "Passed", - // Using gopacket //Protocol: layers.IPProtocol(data[16]).String(), } @@ -354,6 +353,8 @@ func parseByteData(data []byte) types.Packet { pk.DestIP = dstIP.String() } + pk.Direction = config.DirectionIntToString(uint8(data[18])) + isDropped := data[17] if isDropped == 1 { pk.Status = "Dropped" diff --git a/sample/dropit.yaml b/sample/dropit.yaml index b4bd802..52e0bdb 100644 --- a/sample/dropit.yaml +++ b/sample/dropit.yaml @@ -1,6 +1,13 @@ rules: - - id: "rule1" + - id: "drop-all-ingress" sourceIP: '*' destinationPort: '*' - sourcePort: 8080 + sourcePort: '*' protocol: '*' + direction: ingress + - id: "drop-all-egress" + sourceIP: '*' + destinationPort: '*' + sourcePort: '*' + protocol: '*' + direction: egress From 8fd87180b4d68a506c7907565c9b59bded392e94 Mon Sep 17 00:00:00 2001 From: Ashish Tiwari Date: Thu, 21 Sep 2023 02:17:14 +0530 Subject: [PATCH 4/4] add log for active rules --- pkg/monitor/monitor.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/pkg/monitor/monitor.go b/pkg/monitor/monitor.go index eaa9b78..6cc8ebe 100644 --- a/pkg/monitor/monitor.go +++ b/pkg/monitor/monitor.go @@ -8,6 +8,7 @@ import ( "os" "os/signal" "reflect" + "strings" "syscall" "unsafe" @@ -108,7 +109,11 @@ func (m *Monitor) loadConfig(cfgPath string) error { if err != nil { return err } - + rules := []string{} + for rulename := range m.FilterRuleBytesMap { + rules = append(rules, rulename) + } + log.Printf("Currently active rules: %s\n", strings.Join(rules, ",")) viper.OnConfigChange(m.configChangeHandler) viper.WatchConfig() @@ -227,8 +232,11 @@ func (m *Monitor) configChangeHandler(in fsnotify.Event) { log.Println("Filter file updated. No changes to do...") return } - - log.Printf("Filter file updated. Getting new filter rules...\n") + rules := []string{} + for rulename := range frMapNew { + rules = append(rules, rulename) + } + log.Printf("Filter file updated. Currently active rules: %s\n", strings.Join(rules, ",")) for newRule, newVal := range frMapNew { if _, ok := m.FilterRuleBytesMap[newRule]; ok {