Add dns_matching example with test case for for unroll, as requested at iovisor-dev...
authorValkum <rudi.floren@googlemail.com>
Wed, 11 May 2016 12:47:35 +0000 (14:47 +0200)
committerValkum <rudi.floren@googlemail.com>
Wed, 11 May 2016 12:47:35 +0000 (14:47 +0200)
examples/networking/dns_matching/dns_matching.c [new file with mode: 0644]
examples/networking/dns_matching/dns_matching.py [new file with mode: 0644]

diff --git a/examples/networking/dns_matching/dns_matching.c b/examples/networking/dns_matching/dns_matching.c
new file mode 100644 (file)
index 0000000..924460c
--- /dev/null
@@ -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 <uapi/linux/bpf.h>
+#include <uapi/linux/if_ether.h>
+#include <uapi/linux/if_packet.h>
+#include <uapi/linux/ip.h>
+#include <uapi/linux/in.h>
+#include <uapi/linux/udp.h>
+#include <bcc/proto.h>
+
+#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 (file)
index 0000000..335db2c
--- /dev/null
@@ -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()