Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ssl parser to samples/bpf #1

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions samples/bpf/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ hostprogs-y += task_fd_query
hostprogs-y += xdp_sample_pkts
hostprogs-y += ibumad
hostprogs-y += hbm
hostprogs-y += xdp_ssl_parser

# Libbpf dependencies
LIBBPF = $(TOOLS_PATH)/lib/bpf/libbpf.a
Expand Down Expand Up @@ -109,6 +110,7 @@ task_fd_query-objs := bpf_load.o task_fd_query_user.o $(TRACE_HELPERS)
xdp_sample_pkts-objs := xdp_sample_pkts_user.o $(TRACE_HELPERS)
ibumad-objs := bpf_load.o ibumad_user.o $(TRACE_HELPERS)
hbm-objs := bpf_load.o hbm.o $(CGROUP_HELPERS)
xdp_ssl_parser-objs := xdp_ssl_parser_user.o

# Tell kbuild to always build the programs
always := $(hostprogs-y)
Expand Down Expand Up @@ -170,6 +172,7 @@ always += xdp_sample_pkts_kern.o
always += ibumad_kern.o
always += hbm_out_kern.o
always += hbm_edt_kern.o
always += xdp_ssl_parser_kern.o

KBUILD_HOSTCFLAGS += -I$(objtree)/usr/include
KBUILD_HOSTCFLAGS += -I$(srctree)/tools/lib/bpf/
Expand Down
8 changes: 8 additions & 0 deletions samples/bpf/xdp_ssl_parser_common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef _XDP_SSL_PARSER_COMMON_H
#define _XDP_SSL_PARSER_COMMON_H

struct sslsni_wrapper {
char sslsni[100];
};

#endif
290 changes: 290 additions & 0 deletions samples/bpf/xdp_ssl_parser_kern.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
#define KBUILD_MODNAME "foo"

#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/if_packet.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/in.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <uapi/linux/bpf.h>
#include <net/ip.h>
#include <linux/bpf.h>
#include "bpf_helpers.h"
#include "bpf_endian.h"
#include "xdp_ssl_parser_common.h"

#define RANDLEN 32
#define SNIMAXLEN 253
#define MAXSSLEXTS 53

#define PIN_GLOBAL_NS 2
struct bpf_elf_map {
__u32 type;
__u32 size_key;
__u32 size_value;
__u32 max_elem;
__u32 flags;
__u32 id;
__u32 pinning;
};

struct bpf_elf_map SEC("maps") snimap = {
.type = BPF_MAP_TYPE_HASH,
.size_key = sizeof(u32),
.size_value = sizeof(struct sslsni_wrapper),
.max_elem = 1,
.pinning = PIN_GLOBAL_NS,
};

struct __attribute__((__packed__)) sslhdr1 {
__u8 type;
__u16 version;
__u16 len;
};

struct __attribute__((__packed__))sslhandshake {
__u32 type:8,
len:24;
__u16 version;
};

struct sslsession {
__u8 len;
};

struct sslcipher {
__u16 len;
};

struct exthdr {
__u16 id;
__u16 len;
__u16 workaround;
};

struct sniexthdr {
__u16 len;
};

static int parse_ext(void *data, uint64_t *ext_off, void *data_end, __u16 *snilen)
{
struct exthdr *sslext;
__u16 *len;
__u16 extlen;
/* check possible access to offset outside of packet*/
if (data + 1 > data_end)
return -1;

/* check that needed to be made to bypass verifier(possible bug?) */
if (*ext_off > 2000)
return -1;

/* check that needed to be made to bypass verifier(possible bug?) */
if (*ext_off < 1)
return -1;

data += *ext_off;
sslext = (struct exthdr *) data;

/* check possible access to offset outside of packet*/
if (&(sslext->workaround) > data_end)
return -1;

extlen = bpf_htons(sslext->len);
/* check that needed to be made to bypass verifier(possible bug?) */
if (extlen > 2000)
return -1;

/* found server name extension header */
if (bpf_htons(sslext->id) == 0) {
/* check that needed to be made to bypass verifier(possible bug?) */
if (*ext_off > 2000)
return -1;

len = data + 7;
/* check possible access to offset outside of packet*/
if (len + 1 > data_end)
return -1;

*snilen = bpf_htons(*len);
return 1;
}

*ext_off += extlen + 4;

/* check possible access to offset outside of packet*/
if (data + 1 > data_end)
return -1;

return -2;
}

static int ssl(void *data, uint64_t ssl_off, void *data_end)
{
struct sslhdr1 *sslh;
struct sslhandshake *sslhsk;
struct sslsession *sesh;
struct sslcipher *sslci;
uint64_t acc_off = ssl_off;
struct exthdr *sslext;
struct sniexthdr *snihdr;
struct sniexthdr *snihdraux;
struct sslsni_wrapper wrapper = {};
int ret_parse_ext = 0;
__u16 cilen = 10;
__u16 snilen = 0;
__u16 extlen = 0;
u32 key = 0;
char *sslsni;

sslh = data + acc_off;
/* check possible access to offset outside of packet*/
if (sslh + 1 > data_end)
return -1;

/* check if content type is handshake*/
if (sslh->type != 0x16)
return -1;
acc_off += sizeof(struct sslhdr1);

sslhsk = (void *) data + acc_off;

/* check possible access to offset outside of packet*/
if (sslhsk + 1 > data_end)
return -1;

/* check if handshake type is Client Hello*/
if (sslhsk->type != 0x01)
return -1;

acc_off += sizeof(struct sslhandshake) + RANDLEN;

sesh = (void *) data + acc_off;

/* check possible access to offset outside of packet*/
if (sesh + 1 > data_end)
return -1;

acc_off += sizeof(struct sslsession) + sesh->len;
sslci = (void *) data + acc_off;

/* check possible access to offset outside of packet*/
if (sslci + 1 > data_end)
return -1;

cilen = bpf_htons(sslci->len);
/* check that needed to be made to bypass verifier(possible bug?) */
if (cilen > 2000)
return -1;

acc_off += sizeof(struct sslcipher) + cilen + 4;
#pragma clang loop unroll(full)
for (int i = 0; i < MAXSSLEXTS; i++) {
ret_parse_ext = parse_ext(data, &acc_off, data_end, &snilen);
/* found server name extension header */
if(ret_parse_ext == 1)
break;
/* one of the checks in parse_ext function failed*/
else if(ret_parse_ext == -1)
return -1;

/* check that needed to be made to bypass verifier(possible bug?) */
if (acc_off > 2000)
return -1;
}

/* check that needed to be made to bypass verifier(possible bug?) */
if (acc_off > 2000)
return -1;

data += acc_off;
/* check possible access to offset outside of packet*/
if (data + 11 > data_end)
return -1;
sslsni = data + 9;
#pragma clang loop unroll(full)
for (int i = 0; i < 100; i++) {
/* read entire sslsni*/
if (i == snilen) {
wrapper.sslsni[i] = '\0';
bpf_map_update_elem(&snimap, &key, &wrapper, BPF_ANY);
break;
}

/* check possible access to offset outside of packet*/
if (sslsni + 1 > data_end)
return -1;
wrapper.sslsni[i] = *sslsni;
sslsni += 1;
}
return 2;
}

static int tcp(void *data, uint64_t tp_off, void *data_end)
{
struct tcphdr *tcp = data + tp_off;

if (tcp + 1 > data_end)
return 0;

return ssl(data, tp_off + tcp->doff * 4, data_end);
}

static int parse_ipv4(void *data, uint64_t nh_off, void *data_end)
{

struct iphdr *iph = data + nh_off;
uint64_t ihl_len;

if (iph + 1 > data_end)
return 0;

ihl_len = iph->ihl * 4;
return tcp(data, nh_off + ihl_len, data_end);
}

SEC("sslparser")
int handle_ingress(struct xdp_md *ctx)
{
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct ethhdr *eth = data;
int rc = XDP_PASS;
long *value;
u16 h_proto;
u64 nh_off;
u32 ipproto;

nh_off = sizeof(*eth);
if (data + nh_off > data_end)
return rc;

h_proto = eth->h_proto;

if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) {
struct vlan_hdr *vhdr;

vhdr = data + nh_off;
nh_off += sizeof(struct vlan_hdr);
if (data + nh_off > data_end)
return rc;
h_proto = vhdr->h_vlan_encapsulated_proto;
}
if (h_proto == htons(ETH_P_8021Q) || h_proto == htons(ETH_P_8021AD)) {
struct vlan_hdr *vhdr;

vhdr = data + nh_off;
nh_off += sizeof(struct vlan_hdr);
if (data + nh_off > data_end)
return rc;
h_proto = vhdr->h_vlan_encapsulated_proto;
}

if (h_proto == htons(ETH_P_IP))
parse_ipv4(data, nh_off, data_end);

return rc;
}
char _license[] SEC("license") = "GPL";
29 changes: 29 additions & 0 deletions samples/bpf/xdp_ssl_parser_user.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include <linux/unistd.h>
#include <linux/bpf.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <bpf/bpf.h>
#include "xdp_ssl_parser_common.h"

int main(int argc, char **argv) {
int fd = -1;
int key = 0;
const char *map_filename = "/sys/fs/bpf/xdp/globals/snimap";
struct sslsni_wrapper wrapper = {};

fd = bpf_obj_get(map_filename);
if (fd < 0) {
perror("unable to get map obj");
return 1;
}

if (bpf_map_lookup_elem(fd, &key, &wrapper) < 0) {
perror("unable to do map lookup");
return 1;
}

printf("sni %s\n", wrapper.sslsni);
return 0;
}