From 216eaa9b0988f77cb01d4bc37c4637a0120bf921 Mon Sep 17 00:00:00 2001 From: Vincent Li Date: Mon, 9 Sep 2024 19:55:59 +0000 Subject: [PATCH] xdp-dns: lpm trie for domain lookup 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 https://github.com/vincentmli/xdp-tools/issues/1 https://github.com/vincentmli/xdp-tools/issues/2 https://github.com/vincentmli/BPFire/issues/36 Signed-off-by: Vincent Li --- xdp-dns/xdp_dns.bpf.c | 63 ++++++++++++++++++++++++++--------------- xdp-dns/xdp_dns.c | 65 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 97 insertions(+), 31 deletions(-) diff --git a/xdp-dns/xdp_dns.bpf.c b/xdp-dns/xdp_dns.bpf.c index 8339aeaa..8c43d8e3 100644 --- a/xdp-dns/xdp_dns.bpf.c +++ b/xdp-dns/xdp_dns.bpf.c @@ -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 @@ -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; @@ -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 */ @@ -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; @@ -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; @@ -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; } diff --git a/xdp-dns/xdp_dns.c b/xdp-dns/xdp_dns.c index 666aea6a..4c7dfd7a 100644 --- a/xdp-dns/xdp_dns.c +++ b/xdp-dns/xdp_dns.c @@ -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 . + */ + #include #include #include @@ -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; @@ -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 \n", argv[0]); + if (argc != 3) { + fprintf(stderr, "Usage: %s \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"); @@ -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; } -