From a19365176ae4a80dc4fe625a3221ac0de25264ad Mon Sep 17 00:00:00 2001 From: Mike Goldsmth Date: Sat, 15 Jul 2023 08:01:04 +0100 Subject: [PATCH 1/2] wip: http socket filter --- bpf/socket/bpf_bpfel_arm64.go | 120 +++++++++++++++++ bpf/socket/bpf_bpfel_x86.go | 120 +++++++++++++++++ bpf/socket/socket.c | 238 ++++++++++++++++++++++++++++++++++ bpf/socket/socket.go | 103 +++++++++++++++ go.mod | 2 +- go.sum | 4 +- main.go | 5 +- 7 files changed, 587 insertions(+), 5 deletions(-) create mode 100644 bpf/socket/bpf_bpfel_arm64.go create mode 100644 bpf/socket/bpf_bpfel_x86.go create mode 100644 bpf/socket/socket.c create mode 100644 bpf/socket/socket.go diff --git a/bpf/socket/bpf_bpfel_arm64.go b/bpf/socket/bpf_bpfel_arm64.go new file mode 100644 index 00000000..a8738998 --- /dev/null +++ b/bpf/socket/bpf_bpfel_arm64.go @@ -0,0 +1,120 @@ +// Code generated by bpf2go; DO NOT EDIT. +//go:build arm64 +// +build arm64 + +package socket + +import ( + "bytes" + _ "embed" + "fmt" + "io" + + "github.com/cilium/ebpf" +) + +// loadBpf returns the embedded CollectionSpec for bpf. +func loadBpf() (*ebpf.CollectionSpec, error) { + reader := bytes.NewReader(_BpfBytes) + spec, err := ebpf.LoadCollectionSpecFromReader(reader) + if err != nil { + return nil, fmt.Errorf("can't load bpf: %w", err) + } + + return spec, err +} + +// loadBpfObjects loads bpf and converts it into a struct. +// +// The following types are suitable as obj argument: +// +// *bpfObjects +// *bpfPrograms +// *bpfMaps +// +// See ebpf.CollectionSpec.LoadAndAssign documentation for details. +func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error { + spec, err := loadBpf() + if err != nil { + return err + } + + return spec.LoadAndAssign(obj, opts) +} + +// bpfSpecs contains maps and programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type bpfSpecs struct { + bpfProgramSpecs + bpfMapSpecs +} + +// bpfSpecs contains programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type bpfProgramSpecs struct { + SocketHttpFilter *ebpf.ProgramSpec `ebpf:"socket__http_filter"` +} + +// bpfMapSpecs contains maps before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type bpfMapSpecs struct { + Events *ebpf.MapSpec `ebpf:"events"` +} + +// bpfObjects contains all objects after they have been loaded into the kernel. +// +// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. +type bpfObjects struct { + bpfPrograms + bpfMaps +} + +func (o *bpfObjects) Close() error { + return _BpfClose( + &o.bpfPrograms, + &o.bpfMaps, + ) +} + +// bpfMaps contains all maps after they have been loaded into the kernel. +// +// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. +type bpfMaps struct { + Events *ebpf.Map `ebpf:"events"` +} + +func (m *bpfMaps) Close() error { + return _BpfClose( + m.Events, + ) +} + +// bpfPrograms contains all programs after they have been loaded into the kernel. +// +// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. +type bpfPrograms struct { + SocketHttpFilter *ebpf.Program `ebpf:"socket__http_filter"` +} + +func (p *bpfPrograms) Close() error { + return _BpfClose( + p.SocketHttpFilter, + ) +} + +func _BpfClose(closers ...io.Closer) error { + for _, closer := range closers { + if err := closer.Close(); err != nil { + return err + } + } + return nil +} + +// Do not access this directly. +// +//go:embed bpf_bpfel_arm64.o +var _BpfBytes []byte diff --git a/bpf/socket/bpf_bpfel_x86.go b/bpf/socket/bpf_bpfel_x86.go new file mode 100644 index 00000000..97db3304 --- /dev/null +++ b/bpf/socket/bpf_bpfel_x86.go @@ -0,0 +1,120 @@ +// Code generated by bpf2go; DO NOT EDIT. +//go:build 386 || amd64 +// +build 386 amd64 + +package socket + +import ( + "bytes" + _ "embed" + "fmt" + "io" + + "github.com/cilium/ebpf" +) + +// loadBpf returns the embedded CollectionSpec for bpf. +func loadBpf() (*ebpf.CollectionSpec, error) { + reader := bytes.NewReader(_BpfBytes) + spec, err := ebpf.LoadCollectionSpecFromReader(reader) + if err != nil { + return nil, fmt.Errorf("can't load bpf: %w", err) + } + + return spec, err +} + +// loadBpfObjects loads bpf and converts it into a struct. +// +// The following types are suitable as obj argument: +// +// *bpfObjects +// *bpfPrograms +// *bpfMaps +// +// See ebpf.CollectionSpec.LoadAndAssign documentation for details. +func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error { + spec, err := loadBpf() + if err != nil { + return err + } + + return spec.LoadAndAssign(obj, opts) +} + +// bpfSpecs contains maps and programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type bpfSpecs struct { + bpfProgramSpecs + bpfMapSpecs +} + +// bpfSpecs contains programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type bpfProgramSpecs struct { + SocketHttpFilter *ebpf.ProgramSpec `ebpf:"socket__http_filter"` +} + +// bpfMapSpecs contains maps before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type bpfMapSpecs struct { + Events *ebpf.MapSpec `ebpf:"events"` +} + +// bpfObjects contains all objects after they have been loaded into the kernel. +// +// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. +type bpfObjects struct { + bpfPrograms + bpfMaps +} + +func (o *bpfObjects) Close() error { + return _BpfClose( + &o.bpfPrograms, + &o.bpfMaps, + ) +} + +// bpfMaps contains all maps after they have been loaded into the kernel. +// +// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. +type bpfMaps struct { + Events *ebpf.Map `ebpf:"events"` +} + +func (m *bpfMaps) Close() error { + return _BpfClose( + m.Events, + ) +} + +// bpfPrograms contains all programs after they have been loaded into the kernel. +// +// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign. +type bpfPrograms struct { + SocketHttpFilter *ebpf.Program `ebpf:"socket__http_filter"` +} + +func (p *bpfPrograms) Close() error { + return _BpfClose( + p.SocketHttpFilter, + ) +} + +func _BpfClose(closers ...io.Closer) error { + for _, closer := range closers { + if err := closer.Close(); err != nil { + return err + } + } + return nil +} + +// Do not access this directly. +// +//go:embed bpf_bpfel_x86.o +var _BpfBytes []byte diff --git a/bpf/socket/socket.c b/bpf/socket/socket.c new file mode 100644 index 00000000..36ebd169 --- /dev/null +++ b/bpf/socket/socket.c @@ -0,0 +1,238 @@ +// go:build ignore + +#include "vmlinux.h" +#include "bpf_endian.h" +#include "bpf_helpers.h" +#include "bpf_tracing.h" + +char __license[] SEC("license") = "Dual MIT/GPL"; + +struct sock_common +{ + union + { + struct + { + __be32 skc_daddr; + __be32 skc_rcv_saddr; + }; + }; + union + { + // Padding out union skc_hash. + __u32 _; + }; + union + { + struct + { + __be16 skc_dport; + __u16 skc_num; + }; + }; + short unsigned int skc_family; +}; + +struct sock +{ + struct sock_common __sk_common; +}; + +typedef struct http_event_t +{ + u64 start_time; + u64 end_time; + u32 daddr; + u16 dport; + u32 saddr; + u16 sport; + u64 bytes_sent; +} http_event_t; + +struct +{ + __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); +} events SEC(".maps"); + +SEC("socket/http_filter") +int socket__http_filter(struct __sk_buff *skb) +{ + http_event_t event = {}; + event.start_time = bpf_ktime_get_ns(); + + bpf_perf_event_output(skb, &events, BPF_F_CURRENT_CPU, &event, sizeof(http_event_t)); + return 0; +} + +struct sk_buff +{ + union + { + struct + { + struct sk_buff *next; + struct sk_buff *prev; + union + { + struct net_device *dev; + unsigned long dev_scratch; + }; + }; + struct rb_node rbnode; + struct list_head list; + struct llist_node ll_node; + }; + union + { + struct sock *sk; + int ip_defrag_offset; + }; + union + { + ktime_t tstamp; + u64 skb_mstamp_ns; + }; + char cb[48]; + union + { + struct + { + unsigned long _skb_refdst; + void (*destructor)(struct sk_buff *skb); + }; + struct list_head tcp_tsorted_anchor; +#ifdef CONFIG_NET_SOCK_MSG; + unsigned long _sk_redir; +#endif; + }; +#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE); + unsigned long _nfct; +#endif; + unsigned int len, data_len; + __u16 mac_len, hdr_len; + __u16 queue_mapping; +#ifdef __BIG_ENDIAN_BITFIELD; +#define CLONED_MASK (1 << 7); +#else; +#define CLONED_MASK 1; +#endif; +#define CLONED_OFFSET offsetof(struct sk_buff, __cloned_offset); + __u8 cloned : 1, nohdr : 1, fclone : 2, peeked : 1, head_frag : 1, pfmemalloc : 1, pp_recycle : 1; +#ifdef CONFIG_SKB_EXTENSIONS; + __u8 active_extensions; +#endif; + __u8 pkt_type : 3; + __u8 ignore_df : 1; + __u8 dst_pending_confirm : 1; + __u8 ip_summed : 2; + __u8 ooo_okay : 1; + __u8 mono_delivery_time : 1; +#ifdef CONFIG_NET_CLS_ACT; + __u8 tc_at_ingress : 1; + __u8 tc_skip_classify : 1; +#endif; + __u8 remcsum_offload : 1; + __u8 csum_complete_sw : 1; + __u8 csum_level : 2; + __u8 inner_protocol_type : 1; + __u8 l4_hash : 1; + __u8 sw_hash : 1; +#ifdef CONFIG_WIRELESS; + __u8 wifi_acked_valid : 1; + __u8 wifi_acked : 1; +#endif; + __u8 no_fcs : 1; + __u8 encapsulation : 1; + __u8 encap_hdr_csum : 1; + __u8 csum_valid : 1; +#ifdef CONFIG_IPV6_NDISC_NODETYPE; + __u8 ndisc_nodetype : 2; +#endif; +#if IS_ENABLED(CONFIG_IP_VS); + __u8 ipvs_property : 1; +#endif; +#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) || IS_ENABLED(CONFIG_NF_TABLES); + __u8 nf_trace : 1; +#endif; +#ifdef CONFIG_NET_SWITCHDEV; + __u8 offload_fwd_mark : 1; + __u8 offload_l3_fwd_mark : 1; +#endif; + __u8 redirected : 1; +#ifdef CONFIG_NET_REDIRECT; + __u8 from_ingress : 1; +#endif; +#ifdef CONFIG_NETFILTER_SKIP_EGRESS; + __u8 nf_skip_egress : 1; +#endif; +#ifdef CONFIG_TLS_DEVICE; + __u8 decrypted : 1; +#endif; + __u8 slow_gro : 1; +#if IS_ENABLED(CONFIG_IP_SCTP); + __u8 csum_not_inet : 1; +#endif; +#ifdef CONFIG_NET_SCHED; + __u16 tc_index; +#endif; + u16 alloc_cpu; + union + { + __wsum csum; + struct + { + __u16 csum_start; + __u16 csum_offset; + }; + }; + __u32 priority; + int skb_iif; + __u32 hash; + union + { + u32 vlan_all; + struct + { + __be16 vlan_proto; + __u16 vlan_tci; + }; + }; +#if defined(CONFIG_NET_RX_BUSY_POLL) || defined(CONFIG_XPS); + union + { + unsigned int napi_id; + unsigned int sender_cpu; + }; +#endif; +#ifdef CONFIG_NETWORK_SECMARK; + __u32 secmark; +#endif; + union + { + __u32 mark; + __u32 reserved_tailroom; + }; + union + { + __be16 inner_protocol; + __u8 inner_ipproto; + }; + __u16 inner_transport_header; + __u16 inner_network_header; + __u16 inner_mac_header; + __be16 protocol; + __u16 transport_header; + __u16 network_header; + __u16 mac_header; +#ifdef CONFIG_KCOV; + u64 kcov_handle; +#endif; + sk_buff_data_t tail; + sk_buff_data_t end; + unsigned char *head, *data; + unsigned int truesize; + refcount_t users; +#ifdef CONFIG_SKB_EXTENSIONS; + struct skb_ext *extensions; +#endif; +}; \ No newline at end of file diff --git a/bpf/socket/socket.go b/bpf/socket/socket.go new file mode 100644 index 00000000..4c7ead68 --- /dev/null +++ b/bpf/socket/socket.go @@ -0,0 +1,103 @@ +package socket + +import ( + "bytes" + "encoding/binary" + "errors" + "log" + "os" + "syscall" + "unsafe" + + "github.com/cilium/ebpf/perf" + "golang.org/x/sys/unix" +) + +//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target amd64,arm64 -cc clang -cflags $CFLAGS bpf socket.c + +type Event struct { + StartTime uint64 + EndTime uint64 + Daddr uint32 + Dport uint16 + Saddr uint32 + Sport uint16 + BytesSent uint64 +} + +func Setup() { + // Load pre-compiled programs and maps into the kernel. + objs := bpfObjects{} + if err := loadBpfObjects(&objs, nil); err != nil { + log.Fatalf("loading objects: %v", err) + } + defer objs.Close() + + socketFilter := objs.SocketHttpFilter + + // Deploy socket filter + fd, err := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, int(htons(unix.ETH_P_ALL))) + if err != nil { + log.Fatal("Failed to create socket: ", err) + } + err = syscall.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_ATTACH_BPF, socketFilter.FD()) + if err != nil { + log.Fatal("Failed to attach socket filter: ", err) + } + defer unix.Close(fd) + + // Setup perf event reader to read probe events + reader, err := perf.NewReader(objs.Events, os.Getpagesize()) + if err != nil { + log.Fatalf("failed creating perf reader: %v", err) + } + + log.Println("Waiting for events..") + var event Event + for { + record, err := reader.Read() + if err != nil { + if errors.Is(err, perf.ErrClosed) { + return + } + continue + } + + if record.LostSamples != 0 { + continue + } + + if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil { + log.Println("error parsing perf event", err) + continue + } + + log.Printf("event: %+v\n", event) + + // ev := libhoney.NewEvent() + // ev.AddField("name", "socket_event") + // ev.AddField("duration_ms", (event.EndTime - event.StartTime) / 1_000_000) // convert ns to ms + // ev.AddField("source", fmt.Sprintf("%s:%d", toIP4(event.Saddr), event.Sport)) + // ev.AddField("dest", fmt.Sprintf("%s:%d", toIP4(event.Daddr), event.Dport)) + // ev.AddField("num_bytes", event.BytesSent) + // err = ev.Send() + // if err != nil { + // log.Printf("error sending event: %v\n", err) + // } + } +} + +func isLittleEndian() bool { + var a uint16 = 1 + + return *(*byte)(unsafe.Pointer(&a)) == 1 +} + +func htons(a uint16) uint16 { + if isLittleEndian() { + var arr [2]byte + binary.LittleEndian.PutUint16(arr[:], a) + return binary.BigEndian.Uint16(arr[:]) + } + return a +} \ No newline at end of file diff --git a/go.mod b/go.mod index 7f5923a1..a917339f 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.20 require ( github.com/cilium/ebpf v0.10.0 github.com/honeycombio/libhoney-go v1.20.0 + golang.org/x/sys v0.10.0 ) require ( @@ -14,6 +15,5 @@ require ( github.com/klauspost/compress v1.16.6 // indirect github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - golang.org/x/sys v0.2.0 // indirect gopkg.in/alexcesaro/statsd.v2 v2.0.0 // indirect ) diff --git a/go.sum b/go.sum index 35af7922..76fece91 100644 --- a/go.sum +++ b/go.sum @@ -40,8 +40,8 @@ github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9 github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/alexcesaro/statsd.v2 v2.0.0 h1:FXkZSCZIH17vLCO5sO2UucTHsH9pc+17F6pl3JVCwMc= gopkg.in/alexcesaro/statsd.v2 v2.0.0/go.mod h1:i0ubccKGzBVNBpdGV5MocxyA/XlLUJzA7SLonnE4drU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/main.go b/main.go index 24d61532..e160a9b8 100644 --- a/main.go +++ b/main.go @@ -4,7 +4,7 @@ import ( "log" "os" - "github.com/honeycombio/ebpf-agent/bpf/probes" + "github.com/honeycombio/ebpf-agent/bpf/socket" "github.com/honeycombio/libhoney-go" ) @@ -17,5 +17,6 @@ func main() { defer libhoney.Close() log.Println("Starting Honeypot eBPF Agent") - probes.Setup() + // probes.Setup() + socket.Setup() } From f9bfd6ca240fd0b56132cb062cd04c7776ab1c39 Mon Sep 17 00:00:00 2001 From: Mike Goldsmth Date: Mon, 17 Jul 2023 14:43:07 +0100 Subject: [PATCH 2/2] try example filter from isvisor --- bpf/socket/socket.c | 343 +++++++++++++++++++------------------------- 1 file changed, 148 insertions(+), 195 deletions(-) diff --git a/bpf/socket/socket.c b/bpf/socket/socket.c index 36ebd169..7929bafa 100644 --- a/bpf/socket/socket.c +++ b/bpf/socket/socket.c @@ -7,38 +7,61 @@ char __license[] SEC("license") = "Dual MIT/GPL"; -struct sock_common +// example based off https://github.com/iovisor/bcc/blob/master/examples/networking/http_filter/http-parse-simple.c + +#define cursor_advance(_cursor, _len) \ + ({ void *_tmp = _cursor; _cursor += _len; _tmp; }) +#define IP_TCP 6 +#define ETH_HLEN 14 +#define MIN_HTTP_SIZE 12 + +struct ethernet_t { - union - { - struct - { - __be32 skc_daddr; - __be32 skc_rcv_saddr; - }; - }; - union - { - // Padding out union skc_hash. - __u32 _; - }; - union - { - struct - { - __be16 skc_dport; - __u16 skc_num; - }; - }; - short unsigned int skc_family; + unsigned long long dst : 48; + unsigned long long src : 48; + unsigned int type : 16; +}; + +struct ip_t +{ + unsigned char ver : 4; // byte 0 + unsigned char hlen : 4; + unsigned char tos; + unsigned short tlen; + unsigned short identification; // byte 4 + unsigned short ffo_unused : 1; + unsigned short df : 1; + unsigned short mf : 1; + unsigned short foffset : 13; + unsigned char ttl; // byte 8 + unsigned char nextp; + unsigned short hchecksum; + unsigned int src; // byte 12 + unsigned int dst; // byte 16 }; -struct sock +struct tcp_t { - struct sock_common __sk_common; + unsigned short src_port; // byte 0 + unsigned short dst_port; + unsigned int seq_num; // byte 4 + unsigned int ack_num; // byte 8 + unsigned char offset : 4; // byte 12 + unsigned char reserved : 4; + unsigned char flag_cwr : 1; + unsigned char flag_ece : 1; + unsigned char flag_urg : 1; + unsigned char flag_ack : 1; + unsigned char flag_psh : 1; + unsigned char flag_rst : 1; + unsigned char flag_syn : 1; + unsigned char flag_fin : 1; + unsigned short rcv_wnd; + unsigned short cksum; // byte 16 + unsigned short urg_ptr; }; -typedef struct http_event_t +struct http_event_t { u64 start_time; u64 end_time; @@ -47,7 +70,7 @@ typedef struct http_event_t u32 saddr; u16 sport; u64 bytes_sent; -} http_event_t; +}; struct { @@ -57,182 +80,112 @@ struct SEC("socket/http_filter") int socket__http_filter(struct __sk_buff *skb) { - http_event_t event = {}; - event.start_time = bpf_ktime_get_ns(); + u8 *cursor = 0; - bpf_perf_event_output(skb, &events, BPF_F_CURRENT_CPU, &event, sizeof(http_event_t)); - return 0; -} + struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); + // filter IP packets (ethernet type = 0x0800) + if (!(ethernet->type == 0x0800)) + { + goto DROP; + } -struct sk_buff -{ - union + struct ip_t *ip = cursor_advance(cursor, sizeof(*ip)); + // filter TCP packets (ip next protocol = 0x06) + if (ip->nextp != IP_TCP) { - struct - { - struct sk_buff *next; - struct sk_buff *prev; - union - { - struct net_device *dev; - unsigned long dev_scratch; - }; - }; - struct rb_node rbnode; - struct list_head list; - struct llist_node ll_node; - }; - union + goto DROP; + } + + u32 tcp_header_length = 0; + u32 ip_header_length = 0; + u32 payload_offset = 0; + u32 payload_length = 0; + + // calculate ip header length + // value to multiply * 4 + // e.g. ip->hlen = 5 ; IP Header Length = 5 x 4 byte = 20 byte + ip_header_length = ip->hlen << 2; // SHL 2 -> *4 multiply + + // check ip header length against minimum + if (ip_header_length < sizeof(*ip)) { - struct sock *sk; - int ip_defrag_offset; - }; - union + goto DROP; + } + + // shift cursor forward for dynamic ip header size + void *_ = cursor_advance(cursor, (ip_header_length - sizeof(*ip))); + + struct tcp_t *tcp = cursor_advance(cursor, sizeof(*tcp)); + + // calculate tcp header length + // value to multiply *4 + // e.g. tcp->offset = 5 ; TCP Header Length = 5 x 4 byte = 20 byte + tcp_header_length = tcp->offset << 2; // SHL 2 -> *4 multiply + + // calculate payload offset and length + payload_offset = ETH_HLEN + ip_header_length + tcp_header_length; + payload_length = ip->tlen - ip_header_length - tcp_header_length; + + // http://stackoverflow.com/questions/25047905/http-request-minimum-size-in-bytes + // minimum length of http request is always geater than 7 bytes + // avoid invalid access memory + // include empty payload + if (payload_length < 7) { - ktime_t tstamp; - u64 skb_mstamp_ns; - }; - char cb[48]; - union + goto DROP; + } + + // load first 7 byte of payload into p (payload_array) + // direct access to skb not allowed + // unsigned long p[7]; + // int i = 0; + // for (i = 0; i < 7; i++) + // { + // p[i] = load_byte(skb, payload_offset + i); + // } + char p[MIN_HTTP_SIZE]; + bpf_skb_load_bytes(skb, payload_offset, p, sizeof(p)); + + // find a match with an HTTP message + // HTTP + if ((p[0] == 'H') && (p[1] == 'T') && (p[2] == 'T') && (p[3] == 'P')) { - struct - { - unsigned long _skb_refdst; - void (*destructor)(struct sk_buff *skb); - }; - struct list_head tcp_tsorted_anchor; -#ifdef CONFIG_NET_SOCK_MSG; - unsigned long _sk_redir; -#endif; - }; -#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE); - unsigned long _nfct; -#endif; - unsigned int len, data_len; - __u16 mac_len, hdr_len; - __u16 queue_mapping; -#ifdef __BIG_ENDIAN_BITFIELD; -#define CLONED_MASK (1 << 7); -#else; -#define CLONED_MASK 1; -#endif; -#define CLONED_OFFSET offsetof(struct sk_buff, __cloned_offset); - __u8 cloned : 1, nohdr : 1, fclone : 2, peeked : 1, head_frag : 1, pfmemalloc : 1, pp_recycle : 1; -#ifdef CONFIG_SKB_EXTENSIONS; - __u8 active_extensions; -#endif; - __u8 pkt_type : 3; - __u8 ignore_df : 1; - __u8 dst_pending_confirm : 1; - __u8 ip_summed : 2; - __u8 ooo_okay : 1; - __u8 mono_delivery_time : 1; -#ifdef CONFIG_NET_CLS_ACT; - __u8 tc_at_ingress : 1; - __u8 tc_skip_classify : 1; -#endif; - __u8 remcsum_offload : 1; - __u8 csum_complete_sw : 1; - __u8 csum_level : 2; - __u8 inner_protocol_type : 1; - __u8 l4_hash : 1; - __u8 sw_hash : 1; -#ifdef CONFIG_WIRELESS; - __u8 wifi_acked_valid : 1; - __u8 wifi_acked : 1; -#endif; - __u8 no_fcs : 1; - __u8 encapsulation : 1; - __u8 encap_hdr_csum : 1; - __u8 csum_valid : 1; -#ifdef CONFIG_IPV6_NDISC_NODETYPE; - __u8 ndisc_nodetype : 2; -#endif; -#if IS_ENABLED(CONFIG_IP_VS); - __u8 ipvs_property : 1; -#endif; -#if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) || IS_ENABLED(CONFIG_NF_TABLES); - __u8 nf_trace : 1; -#endif; -#ifdef CONFIG_NET_SWITCHDEV; - __u8 offload_fwd_mark : 1; - __u8 offload_l3_fwd_mark : 1; -#endif; - __u8 redirected : 1; -#ifdef CONFIG_NET_REDIRECT; - __u8 from_ingress : 1; -#endif; -#ifdef CONFIG_NETFILTER_SKIP_EGRESS; - __u8 nf_skip_egress : 1; -#endif; -#ifdef CONFIG_TLS_DEVICE; - __u8 decrypted : 1; -#endif; - __u8 slow_gro : 1; -#if IS_ENABLED(CONFIG_IP_SCTP); - __u8 csum_not_inet : 1; -#endif; -#ifdef CONFIG_NET_SCHED; - __u16 tc_index; -#endif; - u16 alloc_cpu; - union + goto KEEP; + } + // GET + if ((p[0] == 'G') && (p[1] == 'E') && (p[2] == 'T')) { - __wsum csum; - struct - { - __u16 csum_start; - __u16 csum_offset; - }; - }; - __u32 priority; - int skb_iif; - __u32 hash; - union + goto KEEP; + } + // POST + if ((p[0] == 'P') && (p[1] == 'O') && (p[2] == 'S') && (p[3] == 'T')) { - u32 vlan_all; - struct - { - __be16 vlan_proto; - __u16 vlan_tci; - }; - }; -#if defined(CONFIG_NET_RX_BUSY_POLL) || defined(CONFIG_XPS); - union + goto KEEP; + } + // PUT + if ((p[0] == 'P') && (p[1] == 'U') && (p[2] == 'T')) { - unsigned int napi_id; - unsigned int sender_cpu; - }; -#endif; -#ifdef CONFIG_NETWORK_SECMARK; - __u32 secmark; -#endif; - union + goto KEEP; + } + // DELETE + if ((p[0] == 'D') && (p[1] == 'E') && (p[2] == 'L') && (p[3] == 'E') && (p[4] == 'T') && (p[5] == 'E')) { - __u32 mark; - __u32 reserved_tailroom; - }; - union + goto KEEP; + } + // HEAD + if ((p[0] == 'H') && (p[1] == 'E') && (p[2] == 'A') && (p[3] == 'D')) { - __be16 inner_protocol; - __u8 inner_ipproto; - }; - __u16 inner_transport_header; - __u16 inner_network_header; - __u16 inner_mac_header; - __be16 protocol; - __u16 transport_header; - __u16 network_header; - __u16 mac_header; -#ifdef CONFIG_KCOV; - u64 kcov_handle; -#endif; - sk_buff_data_t tail; - sk_buff_data_t end; - unsigned char *head, *data; - unsigned int truesize; - refcount_t users; -#ifdef CONFIG_SKB_EXTENSIONS; - struct skb_ext *extensions; -#endif; -}; \ No newline at end of file + goto KEEP; + } + + // no HTTP match + goto DROP; + +// keep the packet and send it to userspace returning -1 +KEEP: + return -1; + +// drop the packet returning 0 +DROP: + return 0; +}