Skip to content

Commit

Permalink
xdp-dns: lpm trie for domain lookup
Browse files Browse the repository at this point in the history
if insert 6bpfire3net in LPM trie map

domain 3foo6bpfire3net will not match because only the
common suffix 6bpfire3net matches, LPM trie is for prefix
match, need to reverse the suffix to prefix, which is to
reverse the 6bpfire3net string and insert in LPM trie map

then reverse 3foo6bpfire3net and do LPM trie map lookup
which will match the common prefix with reversed 6bpfire3net

see more detail in

#1
#2
vincentmli/BPFire#36

Signed-off-by: Vincent Li <[email protected]>
  • Loading branch information
vincentmli committed Sep 12, 2024
1 parent 872c9b9 commit 216eaa9
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 31 deletions.
63 changes: 41 additions & 22 deletions xdp-dns/xdp_dns.bpf.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2021, NLnet Labs. All rights reserved.
* Copyright (c) 2024, BPFire. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -40,7 +41,7 @@
// do not use libc includes because this causes clang
// to include 32bit headers on 64bit ( only ) systems.
#define memcpy __builtin_memcpy
#define MAX_DOMAIN_SIZE 18
#define MAX_DOMAIN_SIZE 128

struct meta_data {
__u16 eth_proto;
Expand All @@ -49,14 +50,22 @@ struct meta_data {
__u16 unused;
};

/* Define the LPM Trie Map for domain names */
struct domain_key {
struct bpf_lpm_trie_key lpm_key;
char data[MAX_DOMAIN_SIZE + 1];
};

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, char[MAX_DOMAIN_SIZE + 1]);
__uint(type, BPF_MAP_TYPE_LPM_TRIE);
__type(key, struct domain_key);
__type(value, __u8);
__uint(max_entries, 1024);
__uint(max_entries, 10000);
__uint(pinning, LIBBPF_PIN_BY_NAME);
__uint(map_flags, BPF_F_NO_PREALLOC);
} domain_denylist SEC(".maps");


/*
* Store the VLAN header
*/
Expand Down Expand Up @@ -191,8 +200,16 @@ static __always_inline __u8 custom_strlen(const char *str, struct cursor *c) {
return len;
}

static __always_inline void reverse_string(char *str, __u8 len) {
for (int i = 0; i < (len - 1) / 2; i++) {
char temp = str[i];
str[i] = str[len - 1 - i];
str[len - 1 - i] = temp;
}
}

SEC("xdp")
int xdp_dns(struct xdp_md *ctx)
int xdp_dns_denylist(struct xdp_md *ctx)
{
struct meta_data *md = (void *)(long)ctx->data_meta;
struct cursor c;
Expand All @@ -201,9 +218,10 @@ int xdp_dns(struct xdp_md *ctx)
struct udphdr *udp;
struct dnshdr *dns;
char *qname;
// __u8 value = 1;
//__u8 value = 1;
__u8 len = 0;
char domain_key[MAX_DOMAIN_SIZE + 1 ] = {0}; // Buffer for map lookup

struct domain_key dkey = {0}; // LPM trie key

if (bpf_xdp_adjust_meta(ctx, -(int)sizeof(struct meta_data)))
return XDP_PASS;
Expand Down Expand Up @@ -246,26 +264,27 @@ int xdp_dns(struct xdp_md *ctx)
return XDP_ABORTED; // Return FORMERR?

int copy_len = len < MAX_DOMAIN_SIZE ? len : MAX_DOMAIN_SIZE;
custom_memcpy(domain_key, qname, copy_len);
domain_key[MAX_DOMAIN_SIZE] = '\0'; // Ensure null-termination
custom_memcpy(dkey.data, qname, copy_len);
dkey.data[MAX_DOMAIN_SIZE] = '\0'; // Ensure null-termination
reverse_string(dkey.data, copy_len);

// Set the LPM key prefix length (the length of the domain name string)
dkey.lpm_key.prefixlen = copy_len * 8; // Prefix length in bits

//bpf_printk("domain_key %s copy_len is %d from %pI4", dkey.data, copy_len, &ipv4->saddr);

if (bpf_map_lookup_elem(&domain_denylist, &dkey)) {
bpf_printk("Domain %s found in denylist, dropping packet\n", dkey.data);
return XDP_DROP;
}

/*
bpf_printk("domain_key %s copy_len is %d from %pI4", domain_key, copy_len, &ipv4->saddr);
if (bpf_map_update_elem(&domain_denylist, &domain_key, &value, BPF_ANY) < 0) {
bpf_printk("Domain %s not updated in denylist\n", domain_key);
if (bpf_map_update_elem(&domain_denylist, &dkey, &value, BPF_ANY) < 0) {
bpf_printk("Domain %s not updated in denylist\n", dkey.data);
} else {
bpf_printk("Domain %s updated in denylist\n", domain_key);
bpf_printk("Domain %s updated in denylist\n", dkey.data);
}
*/
if (bpf_map_lookup_elem(&domain_denylist, domain_key)) {
bpf_printk("Domain %s found in denylist, dropping packet\n", domain_key);
return XDP_DROP;
}
else {
bpf_printk("Domain %s not found in denylist\n", domain_key);
}

break;
}
Expand Down
65 changes: 56 additions & 9 deletions xdp-dns/xdp_dns.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
/*
* Copyright (c) 2024, BPFire. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <stdio.h>
Expand All @@ -7,6 +24,11 @@

#define MAX_DOMAIN_SIZE 128 // Increased size to handle larger domains

struct domain_key {
struct bpf_lpm_trie_key lpm_key;
char data[MAX_DOMAIN_SIZE + 1];
};

// Function to encode a domain name with label lengths
static void encode_domain(const char *domain, char *encoded) {
const char *ptr = domain;
Expand All @@ -33,19 +55,32 @@ static void encode_domain(const char *domain, char *encoded) {
*enc_ptr++ = 0;
}

static void reverse_string(char *str) {
int len = strlen(str);
for (int i = 0; i < len / 2; i++) {
char temp = str[i];
str[i] = str[len - i - 1];
str[len - i - 1] = temp;
}
}

int main(int argc, char *argv[]) {
int map_fd;
char domain_key[MAX_DOMAIN_SIZE + 1] = {0};
struct domain_key dkey = {0};
__u8 value = 1;

// Check for proper number of arguments
if (argc != 2) {
fprintf(stderr, "Usage: %s <domain>\n", argv[0]);
if (argc != 3) {
fprintf(stderr, "Usage: %s <add|delete> <domain>\n", argv[0]);
return 1;
}

// Encode the domain name with label lengths
encode_domain(argv[1], domain_key);
encode_domain(argv[2], dkey.data);
reverse_string(dkey.data);

// Set the LPM trie key prefix length
dkey.lpm_key.prefixlen = strlen(dkey.data) * 8;

// Open the BPF map
map_fd = bpf_obj_get("/sys/fs/bpf/xdp-dns/domain_denylist");
Expand All @@ -54,13 +89,25 @@ int main(int argc, char *argv[]) {
return 1;
}

// Update the map with the encoded domain name
if (bpf_map_update_elem(map_fd, domain_key, &value, BPF_ANY) != 0) {
fprintf(stderr, "Failed to update map: %s\n", strerror(errno));
// Add or delete the domain based on the first argument
if (strcmp(argv[1], "add") == 0) {
// Update the map with the encoded domain name
if (bpf_map_update_elem(map_fd, &dkey, &value, BPF_ANY) != 0) {
fprintf(stderr, "Failed to add domain to map: %s\n", strerror(errno));
return 1;
}
printf("Domain %s added to denylist\n", argv[2]);
} else if (strcmp(argv[1], "delete") == 0) {
// Remove the domain from the map
if (bpf_map_delete_elem(map_fd, &dkey) != 0) {
fprintf(stderr, "Failed to remove domain from map: %s\n", strerror(errno));
return 1;
}
printf("Domain %s removed from denylist\n", argv[2]);
} else {
fprintf(stderr, "Invalid command: %s. Use 'add' or 'delete'.\n", argv[1]);
return 1;
}

printf("Domain %s added to denylist\n", argv[1]);
return 0;
}

0 comments on commit 216eaa9

Please sign in to comment.