diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 1d9be26b4edd79..accffa8463c5b6 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -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 @@ -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) @@ -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/ diff --git a/samples/bpf/xdp_ssl_parser_common.h b/samples/bpf/xdp_ssl_parser_common.h new file mode 100644 index 00000000000000..af9ed17e07cb89 --- /dev/null +++ b/samples/bpf/xdp_ssl_parser_common.h @@ -0,0 +1,8 @@ +#ifndef _XDP_SSL_PARSER_COMMON_H +#define _XDP_SSL_PARSER_COMMON_H + +struct sslsni_wrapper { + char sslsni[100]; +}; + +#endif diff --git a/samples/bpf/xdp_ssl_parser_kern.c b/samples/bpf/xdp_ssl_parser_kern.c new file mode 100644 index 00000000000000..8895c9eacf6286 --- /dev/null +++ b/samples/bpf/xdp_ssl_parser_kern.c @@ -0,0 +1,290 @@ +#define KBUILD_MODNAME "foo" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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"; diff --git a/samples/bpf/xdp_ssl_parser_user.c b/samples/bpf/xdp_ssl_parser_user.c new file mode 100644 index 00000000000000..955355fe84549e --- /dev/null +++ b/samples/bpf/xdp_ssl_parser_user.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include +#include +#include +#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; +}