Skip to content

Commit

Permalink
xdp-tailcall: add DNS XDP program
Browse files Browse the repository at this point in the history
add DNS XDP program as tail called program

Signed-off-by: Vincent Li <[email protected]>
  • Loading branch information
vincentmli committed Jan 6, 2025
1 parent ad2a4e6 commit 74f1856
Show file tree
Hide file tree
Showing 5 changed files with 641 additions and 1 deletion.
2 changes: 1 addition & 1 deletion xdp-tailcall/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

XDP_TARGETS := xdp_tailcall.bpf
BPF_SKEL_TARGETS := $(XDP_TARGETS)
USER_TARGETS := xdp_sni xdp_sni_log
USER_TARGETS := xdp_sni xdp_sni_log xdp_dns xdp_dns_log

LIB_DIR = ../lib

Expand Down
121 changes: 121 additions & 0 deletions xdp-tailcall/xdp_dns.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* 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>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

#define MAX_DOMAIN_SIZE 63 // 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;
char *enc_ptr = encoded;
size_t label_len;

while (*ptr) {
// Find the length of the current label
label_len = strcspn(ptr, ".");
if (label_len > 0) {
// Set the length of the label
*enc_ptr++ = (char)label_len;
// Copy the label itself
memcpy(enc_ptr, ptr, label_len);
enc_ptr += label_len;
}
// Move to the next label
ptr += label_len;
if (*ptr == '.') {
ptr++; // Skip the dot
}
}
// Append a zero-length label to mark the end of the domain name
*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;
struct domain_key dkey = { 0 };
__u8 value = 1;

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

// Encode the domain name with label lengths
encode_domain(argv[3], 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
const char *map_path = argv[1];
map_fd = bpf_obj_get(map_path);
if (map_fd < 0) {
fprintf(stderr, "Failed to open map at %s: %s\n", map_path, strerror(errno));
return 1;
}

// Add or delete the domain based on the first argument
if (strcmp(argv[2], "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[3]);
} else if (strcmp(argv[2], "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[3]);
} else {
fprintf(stderr, "Invalid command: %s. Use 'add' or 'delete'.\n",
argv[2]);
return 1;
}

return 0;
}
91 changes: 91 additions & 0 deletions xdp-tailcall/xdp_dns.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#define DNS_PORT 53
#define RR_TYPE_OPT 41

#define FRAME_SIZE 1000000000

/*
* Store the DNS header
*/
struct dnshdr {
__u16 id;
union {
struct {
__u8 rd : 1;
__u8 tc : 1;
__u8 aa : 1;
__u8 opcode : 4;
__u8 qr : 1;

__u8 rcode : 4;
__u8 cd : 1;
__u8 ad : 1;
__u8 z : 1;
__u8 ra : 1;
} as_bits_and_pieces;
__u16 as_value;
} flags;
__u16 qdcount;
__u16 ancount;
__u16 nscount;
__u16 arcount;
};

struct dns_qrr {
__u16 qtype;
__u16 qclass;
};

struct dns_rr {
__u16 type;
__u16 class;
__u32 ttl;
__u16 rdata_len;
} __attribute__((packed));

struct option {
__u16 code;
__u16 len;
__u8 data[];
} __attribute__((packed));

/*
* Recalculate the checksum
*/
static __always_inline
void update_checksum(__u16 *csum, __u16 old_val, __u16 new_val)
{
__u32 new_csum_value;
__u32 new_csum_comp;
__u32 undo;

undo = ~((__u32)*csum) + ~((__u32)old_val);
new_csum_value = undo + (undo < ~((__u32)old_val)) + (__u32)new_val;
new_csum_comp = new_csum_value + (new_csum_value < ((__u32)new_val));
new_csum_comp = (new_csum_comp & 0xFFFF) + (new_csum_comp >> 16);
new_csum_comp = (new_csum_comp & 0xFFFF) + (new_csum_comp >> 16);
*csum = (__u16)~new_csum_comp;
}


//TCP

#define NSEC_PER_SEC 1000000000L

#define ETH_ALEN 6
#define ETH_P_IP 0x0800
#define ETH_P_IPV6 0x86DD

#define IP_DF 0x4000
#define IP_MF 0x2000
#define IP_OFFSET 0x1fff

#define swap(a, b) \
do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0)

#define __get_unaligned_t(type, ptr) ({ \
const struct { type x; } __attribute__((__packed__)) *__pptr = (typeof(__pptr))(ptr); \
__pptr->x; \
})

#define get_unaligned(ptr) __get_unaligned_t(typeof(*(ptr)), (ptr))

110 changes: 110 additions & 0 deletions xdp-tailcall/xdp_dns_log.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* 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 <stdio.h>
#include <arpa/inet.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include <syslog.h>

#define MAX_DOMAIN_SIZE 63

struct qname_event {
__u8 len;
__u32 src_ip; // IPv4 address
char qname[MAX_DOMAIN_SIZE + 1];
};

// Helper function to convert DNS label to a standard domain format
void dns_label_to_dot_notation(char *dns_name, char *output, size_t len)
{
size_t pos = 0, out_pos = 0;

while (pos < len) {
__u8 label_len = dns_name[pos];
if (label_len == 0)
break; // End of domain name

if (out_pos != 0) {
output[out_pos++] = '.'; // Add a dot between labels
}

// Copy the label
for (int i = 1; i <= label_len; i++) {
output[out_pos++] = dns_name[pos + i];
}

pos += label_len + 1; // Move to the next label
}

output[out_pos] = '\0'; // Null-terminate the result
}

// Corrected handle_event function to match the signature expected by ring_buffer__new
int handle_event(void *ctx __attribute__((unused)), void *data,
size_t data_sz __attribute__((unused)))
{
struct qname_event *event = (struct qname_event *)data;

char src_ip_str[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &event->src_ip, src_ip_str, sizeof(src_ip_str));

char domain_str[MAX_DOMAIN_SIZE] = { 0 };
dns_label_to_dot_notation(event->qname, domain_str, event->len);

syslog(LOG_INFO, "Received qname: %s from source IP: %s", domain_str,
src_ip_str);

return 0; // Return 0 to indicate success
}

int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "Usage: %s <path_to_ringbuf>\n", argv[0]);
return 1;
}

const char *ringbuf_path = argv[1];
struct ring_buffer *rb;
int ringbuf_fd;

openlog("qname_logger", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);

// Open the ring buffer
ringbuf_fd = bpf_obj_get(ringbuf_path);
if (ringbuf_fd < 0) {
perror("Failed to open ring buffer");
return 1;
}

// Set up ring buffer polling with the corrected function signature
rb = ring_buffer__new(ringbuf_fd, handle_event, NULL, NULL);
if (!rb) {
perror("Failed to create ring buffer");
return 1;
}

// Poll the ring buffer
while (1) {
ring_buffer__poll(rb, -1); // Block indefinitely
}

ring_buffer__free(rb);
closelog();
return 0;
}
Loading

0 comments on commit 74f1856

Please sign in to comment.