summaryrefslogtreecommitdiffstats
path: root/clover/clovisor/libclovisor/ebpf/session_tracking.c
diff options
context:
space:
mode:
Diffstat (limited to 'clover/clovisor/libclovisor/ebpf/session_tracking.c')
-rwxr-xr-xclover/clovisor/libclovisor/ebpf/session_tracking.c292
1 files changed, 292 insertions, 0 deletions
diff --git a/clover/clovisor/libclovisor/ebpf/session_tracking.c b/clover/clovisor/libclovisor/ebpf/session_tracking.c
new file mode 100755
index 0000000..ea68788
--- /dev/null
+++ b/clover/clovisor/libclovisor/ebpf/session_tracking.c
@@ -0,0 +1,292 @@
+// Copyright (c) Authors of Clover
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Apache License, Version 2.0
+// which accompanies this distribution, and is available at
+// http://www.apache.org/licenses/LICENSE-2.0
+#include <uapi/linux/if_ether.h>
+#include <uapi/linux/in.h>
+#include <uapi/linux/ip.h>
+#include <uapi/linux/tcp.h>
+#include <uapi/linux/pkt_cls.h>
+#include <uapi/linux/bpf.h>
+
+#include <bcc/proto.h>
+
+#define HTTP_HDR_MIN_LEN 7
+#define MAX_SESSION_TABLE_ENTRIES 8192
+
+typedef enum {
+ UNDEFINED = 0,
+ HTTP = 1,
+ HTTP2 = 2,
+ TCP = 3,
+ UDP = 4,
+} app_proto_t;
+
+typedef struct session_key_ {
+ u32 src_ip;
+ u32 dst_ip;
+ unsigned short src_port;
+ unsigned short dst_port;
+} session_key_t;
+
+typedef struct session_ {
+ u64 req_time;
+ u64 resp_time;
+} session_t;
+
+typedef struct egress_match_ {
+ u32 dst_ip;
+ unsigned short dst_port;
+} egress_match_t;
+
+typedef enum policy_action_ {
+ RECORD = 1,
+} policy_action_t;
+
+BPF_PERF_OUTPUT(skb_events);
+BPF_HASH(dports2proto, u16, u32);
+BPF_HASH(egress_lookup_table, egress_match_t, policy_action_t);
+BPF_HASH(sessions, session_key_t, session_t, MAX_SESSION_TABLE_ENTRIES);
+
+struct eth_hdr {
+ unsigned char h_dest[ETH_ALEN];
+ unsigned char h_source[ETH_ALEN];
+ unsigned short h_proto;
+};
+
+static inline int ipv4_hdrlen(struct iphdr *ip4)
+{
+ return ip4->ihl * 4;
+}
+
+static inline int tcp_doff(struct tcphdr *tcp_hdr)
+{
+ return tcp_hdr->doff * 4;
+}
+
+static inline int http_parsing(void *data, void *data_end)
+{
+
+ int is_http = 1;
+ if (data + HTTP_HDR_MIN_LEN > data_end) {
+ bpf_trace_printk("No HTTP Header in TCP segment");
+ return 0;
+ }
+ if (strncmp((char*)data, "HTTP", 4)) {
+ if (strncmp((char*)data, "GET", 3)) {
+ if (strncmp((char*)data, "POST", 4)) {
+ if (strncmp((char*)data, "PUT", 3)) {
+ if (strncmp((char*)data, "HEAD", 4)) {
+ is_http = 0;
+ }
+ }
+ }
+ }
+ }
+ return is_http;
+}
+
+static inline void fill_up_sess_key(session_key_t *key, u32 src_ip,
+ u32 dst_ip, u16 src_port, u16 dst_port)
+{
+ key->src_ip = src_ip;
+ key->dst_ip = dst_ip;
+ key->src_port = src_port;
+ key->dst_port = dst_port;
+}
+
+static inline int process_response(u32 src_ip, u32 dst_ip, u16 src_port,
+ u16 dst_port)
+{
+ session_key_t sess_key = {};
+ session_t *session_ptr = NULL;
+ fill_up_sess_key(&sess_key, src_ip, dst_ip, src_port, dst_port);
+ session_ptr = sessions.lookup(&sess_key);
+ if (session_ptr != NULL) {
+ u64 resp_time = bpf_ktime_get_ns();
+ session_t update_session = {
+ session_ptr->req_time,
+ resp_time
+ };
+ sessions.update(&sess_key, &update_session);
+ return 1;
+ }
+ return 0;
+}
+
+static inline void process_request(u32 src_ip, u32 dst_ip, u16 src_port,
+ u16 dst_port)
+{
+ session_key_t sess_key = {};
+ session_t *session_ptr = NULL;
+ session_t new_session = {
+ bpf_ktime_get_ns(),
+ 0
+ };
+ fill_up_sess_key(&sess_key, src_ip, dst_ip, src_port, dst_port);
+ session_ptr = sessions.lookup(&sess_key);
+ if (! session_ptr) {
+ sessions.insert(&sess_key, &new_session);
+ }
+ /*
+ if (session_ptr != NULL) {
+ sessions.update(&sess_key, &new_session);
+ } else {
+ sessions.insert(&sess_key, &new_session);
+ }
+ */
+}
+
+static inline app_proto_t ingress_tcp_parsing(struct tcphdr *tcp_hdr,
+ struct iphdr *ipv4_hdr,
+ void *data_end)
+{
+ unsigned short dest_port = htons(tcp_hdr->dest);
+ egress_match_t egress_match = {};
+ policy_action_t *policy_ptr = NULL;
+ app_proto_t ret = TCP;
+
+ unsigned int *proto = dports2proto.lookup(&dest_port);
+ if (proto != NULL) {
+ /*
+ if (tcp_hdr->syn && !tcp_hdr->ack) {
+ return ret;
+ }
+ */
+ ret = HTTP;
+ if (tcp_hdr->fin || tcp_hdr->rst) {
+ process_response(ntohl(ipv4_hdr->saddr),
+ ntohl(ipv4_hdr->daddr),
+ ntohs(tcp_hdr->source),
+ ntohs(tcp_hdr->dest));
+ } else {
+ process_request(ntohl(ipv4_hdr->saddr),
+ ntohl(ipv4_hdr->daddr),
+ ntohs(tcp_hdr->source),
+ ntohs(tcp_hdr->dest));
+ }
+ } else {
+ dest_port = htons(tcp_hdr->source);
+ proto = dports2proto.lookup(&dest_port);
+ if (proto != NULL) {
+ // clock response receiving time
+ process_response(ntohl(ipv4_hdr->daddr),
+ ntohl(ipv4_hdr->saddr),
+ ntohs(tcp_hdr->dest),
+ ntohs(tcp_hdr->source));
+ }
+ egress_match.dst_ip = ntohl(ipv4_hdr->saddr);
+ egress_match.dst_port = ntohs(tcp_hdr->source);
+ policy_ptr = egress_lookup_table.lookup(&egress_match);
+ if (policy_ptr == NULL) {
+ egress_match.dst_ip = 0;
+ policy_ptr = egress_lookup_table.lookup(&egress_match);
+ }
+
+ if (policy_ptr != NULL) {
+ if (*policy_ptr == RECORD) {
+ ret = HTTP;
+ if (tcp_hdr->fin || tcp_hdr->rst) {
+ process_response(ntohl(ipv4_hdr->daddr),
+ ntohl(ipv4_hdr->saddr),
+ ntohs(tcp_hdr->dest),
+ ntohs(tcp_hdr->source));
+ }
+ }
+ }
+ }
+
+ // everything else drops to TCP
+ //return ((void*)tcp_hdr);
+ return ret;
+}
+
+static inline app_proto_t egress_tcp_parsing(struct tcphdr *tcp_hdr,
+ struct iphdr *ipv4_hdr,
+ void *data_end)
+{
+ unsigned short src_port = htons(tcp_hdr->source);
+ app_proto_t ret = TCP;
+ egress_match_t egress_match = {};
+ policy_action_t *policy_ptr = NULL;
+
+ unsigned int *proto = dports2proto.lookup(&src_port);
+
+ if (proto != NULL) {
+ //if (tcp_hdr->fin || tcp_hdr->rst) {
+ process_response(ntohl(ipv4_hdr->daddr),
+ ntohl(ipv4_hdr->saddr),
+ ntohs(tcp_hdr->dest),
+ ntohs(tcp_hdr->source));
+ //}
+ ret = HTTP;
+ } else {
+
+ egress_match.dst_ip = ntohl(ipv4_hdr->daddr);
+ egress_match.dst_port = ntohs(tcp_hdr->dest);
+ policy_ptr = egress_lookup_table.lookup(&egress_match);
+ if (policy_ptr == NULL) {
+ egress_match.dst_ip = 0;
+ policy_ptr = egress_lookup_table.lookup(&egress_match);
+ }
+
+ if (policy_ptr != NULL) {
+ if (*policy_ptr == RECORD) {
+ process_request(ntohl(ipv4_hdr->saddr),
+ ntohl(ipv4_hdr->daddr),
+ ntohs(tcp_hdr->source),
+ ntohs(tcp_hdr->dest));
+ ret = HTTP;
+ }
+ }
+ }
+ //return(ret_hdr);
+ return ret;
+}
+
+static inline int handle_packet(struct __sk_buff *skb, int is_ingress)
+{
+ void *data = (void *)(long)skb->data;
+ void *data_end = (void *)(long)skb->data_end;
+ struct eth_hdr *eth = data;
+ struct iphdr *ipv4_hdr = data + sizeof(*eth);
+ struct tcphdr *tcp_hdr = data + sizeof(*eth) + sizeof(*ipv4_hdr);
+ app_proto_t proto = TCP;
+
+ /* TODO(s3wong): assuming TCP only for now */
+ /* single length check */
+ if (data + sizeof(*eth) + sizeof(*ipv4_hdr) + sizeof(*tcp_hdr) > data_end)
+ return TC_ACT_OK;
+
+ if (eth->h_proto != htons(ETH_P_IP))
+ return TC_ACT_OK;
+
+ // TODO(s3wong): no support for IP options
+ if (ipv4_hdr->protocol != IPPROTO_TCP || ipv4_hdr->ihl != 5)
+ return TC_ACT_OK;
+
+ if (is_ingress == 1) {
+ proto = ingress_tcp_parsing(tcp_hdr, ipv4_hdr, data_end);
+ } else{
+ proto = egress_tcp_parsing(tcp_hdr, ipv4_hdr, data_end);
+ }
+
+ if (proto == HTTP) {
+ int offset = is_ingress;
+ skb_events.perf_submit_skb(skb, skb->len, &offset, sizeof(offset));
+ }
+
+ return TC_ACT_OK;
+}
+
+int handle_ingress(struct __sk_buff *skb)
+{
+ return handle_packet(skb, 1);
+}
+
+int handle_egress(struct __sk_buff *skb)
+{
+ return handle_packet(skb, 0);
+}