From e3a1c18f457cdb1cc9f482d0ea960f6a099d7b8c Mon Sep 17 00:00:00 2001 From: Valkum Date: Wed, 11 May 2016 14:47:35 +0200 Subject: [PATCH] Add dns_matching example with test case for for unroll, as requested at iovisor-dev@lists.iovisor.org --- examples/networking/dns_matching/dns_matching.c | 105 +++++++++++++++++++++++ examples/networking/dns_matching/dns_matching.py | 57 ++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 examples/networking/dns_matching/dns_matching.c create mode 100644 examples/networking/dns_matching/dns_matching.py diff --git a/examples/networking/dns_matching/dns_matching.c b/examples/networking/dns_matching/dns_matching.c new file mode 100644 index 0000000..924460c --- /dev/null +++ b/examples/networking/dns_matching/dns_matching.c @@ -0,0 +1,105 @@ +/* + * dns_matching.c Drop DNS packets requesting DNS name contained in hash map + * For Linux, uses BCC, eBPF. See .py file. + * + * Copyright (c) 2016 Rudi Floren. + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 11-May-2016 Rudi Floren Created this. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define ETH_LEN 14 + +struct dns_hdr_t +{ + uint16_t id; + uint16_t flags; + uint16_t qdcount; + uint16_t ancount; + uint16_t nscount; + uint16_t arcount; +} BPF_PACKET_HEADER; + + +struct dns_query_flags_t +{ + uint16_t qtype; + uint16_t qclass; +} BPF_PACKET_HEADER; + +struct dns_char_t +{ + char c; +} BPF_PACKET_HEADER; + +struct Key { + unsigned char p[32]; +}; + +struct Leaf { + // Not really needed in this example + unsigned char p[4]; +}; + +BPF_TABLE("hash", struct Key, struct Leaf, cache, 128); + +int dns_test(struct __sk_buff *skb) +{ + u8 *cursor = 0; + struct Key key = {}; + // Check of ethernet/IP frame. + struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet)); + if(ethernet->type == ETH_P_IP) { + + // Check for UDP. + struct ip_t *ip = cursor_advance(cursor, sizeof(*ip)); + u16 hlen_bytes = ip->hlen << 2; + if(ip->nextp == IPPROTO_UDP) { + + // Check for Port 53, DNS packet. + struct udp_t *udp = cursor_advance(cursor, sizeof(*udp)); + if(udp->dport == 53){ + + // Our Cursor + the length of our udp packet - size of the udp header + // - the two 16bit values for QTYPE and QCLASS. + u8 *sentinel = cursor + udp->length - sizeof(*udp) - 4; + + struct dns_hdr_t *dns_hdr = cursor_advance(cursor, sizeof(*dns_hdr)); + + // Do nothing if packet is not a request. + if((dns_hdr->flags >>15) != 0) { + // Exit if this packet is not a request. + return -1; + } + + u16 i = 0; + struct dns_char_t *c; + // This unroll worked not in latest BCC version. + for(u8 j = 0; i<255;i++){ + if (cursor == sentinel) goto end; c = cursor_advance(cursor, 1); key.p[i++] = c->c; + } + end: + {} + + struct Leaf * lookup_leaf = cache.lookup(&key); + + // If DNS name is contained in our map, drop packet. + if(lookup_leaf) { + return 0; + } + } + } + } + + return -1; +} diff --git a/examples/networking/dns_matching/dns_matching.py b/examples/networking/dns_matching/dns_matching.py new file mode 100644 index 0000000..335db2c --- /dev/null +++ b/examples/networking/dns_matching/dns_matching.py @@ -0,0 +1,57 @@ +#!/usr/bin/python + +from __future__ import print_function +from bcc import BPF +from ctypes import * + +import sys +import socket +import os +import struct + + +def encode_dns(name): + size = 32 + if len(name) > 253: + raise Exception("DNS Name too long.") + b = bytearray(size) + i = 0; + elements = name.split(".") + for element in elements: + b[i] = struct.pack("!B", len(element)) + i += 1 + for j in range(0, len(element)): + b[i] = element[j] + i += 1 + + + return (c_ubyte * size).from_buffer(b) + + + +# initialize BPF - load source code from http-parse-simple.c +bpf = BPF(src_file = "dns_matching.c", debug=0) +# print(bpf.dump_func("dns_test")) + +#load eBPF program http_filter of type SOCKET_FILTER into the kernel eBPF vm +#more info about eBPF program types +#http://man7.org/linux/man-pages/man2/bpf.2.html +function_dns_matching = bpf.load_func("dns_matching", BPF.SOCKET_FILTER) + + +#create raw socket, bind it to eth0 +#attach bpf program to socket created +BPF.attach_raw_socket(function_dns_matching, "eth1") + +# Get the table. +cache = bpf.get_table("cache") + +# Create first entry for foo.bar +key = cache.Key() +key.p = encode_dns("foo.bar") + +leaf = cache.Leaf() +leaf.p = (c_ubyte * 4).from_buffer(bytearray(4)) +cache[key] = leaf + +bpf.trace_print() -- 2.7.4