1 /*
2  * dns_matching.c  Drop DNS packets requesting DNS name contained in hash map
3  *    For Linux, uses BCC, eBPF. See .py file.
4  *
5  * Copyright (c) 2016 Rudi Floren.
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of version 2 of the GNU General Public
8  * License as published by the Free Software Foundation.
9  *
10  * 11-May-2016  Rudi Floren Created this.
11  */
12 
13 #include <uapi/linux/bpf.h>
14 #include <uapi/linux/if_ether.h>
15 #include <uapi/linux/if_packet.h>
16 #include <uapi/linux/ip.h>
17 #include <uapi/linux/in.h>
18 #include <uapi/linux/udp.h>
19 #include <bcc/proto.h>
20 
21 #define ETH_LEN 14
22 
23 struct dns_hdr_t
24 {
25     uint16_t id;
26     uint16_t flags;
27     uint16_t qdcount;
28     uint16_t ancount;
29     uint16_t nscount;
30     uint16_t arcount;
31 } BPF_PACKET_HEADER;
32 
33 
34 struct dns_query_flags_t
35 {
36   uint16_t qtype;
37   uint16_t qclass;
38 } BPF_PACKET_HEADER;
39 
40 struct dns_char_t
41 {
42     char c;
43 } BPF_PACKET_HEADER;
44 
45 struct Key {
46   unsigned char p[255];
47 };
48 
49 struct Leaf {
50   // Not really needed in this example
51   unsigned char p[4];
52 };
53 
54 BPF_HASH(cache, struct Key, struct Leaf, 128);
55 
dns_matching(struct __sk_buff * skb)56 int dns_matching(struct __sk_buff *skb)
57 {
58   u8 *cursor = 0;
59   struct Key key = {};
60   // Check of ethernet/IP frame.
61   struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));
62   if(ethernet->type == ETH_P_IP) {
63 
64     // Check for UDP.
65     struct ip_t *ip = cursor_advance(cursor, sizeof(*ip));
66     u16 hlen_bytes = ip->hlen << 2;
67     if(ip->nextp == IPPROTO_UDP) {
68 
69       // Check for Port 53, DNS packet.
70       struct udp_t *udp = cursor_advance(cursor, sizeof(*udp));
71       if(udp->dport == 53){
72 
73         struct dns_hdr_t *dns_hdr = cursor_advance(cursor, sizeof(*dns_hdr));
74 
75         // Do nothing if packet is not a request.
76         if((dns_hdr->flags >>15) != 0) {
77           // Exit if this packet is not a request.
78           return -1;
79         }
80 
81         u16 i = 0;
82         struct dns_char_t *c;
83         #pragma unroll
84         for(i = 0; i<255;i++){
85           c = cursor_advance(cursor, 1);
86           if (c->c == 0)
87             break;
88           key.p[i] = c->c;
89         }
90 
91         struct Leaf * lookup_leaf = cache.lookup(&key);
92 
93         // If DNS name is contained in our map, keep the packet
94         if(lookup_leaf) {
95           bpf_trace_printk("Matched1\n");
96           return -1;
97         }
98       }
99     }
100   }
101   // Drop the packet
102   return 0;
103 }
104