Commit e3a1c18f authored by Valkum's avatar Valkum

Add dns_matching example with test case for for unroll, as requested at...

Add dns_matching example with test case for for unroll, as requested at iovisor-dev@lists.iovisor.org
parent ba275e4c
/*
* 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;
}
#!/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()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment