From f1a0bc096622aaae9a002f62127a1fb0706a55ee Mon Sep 17 00:00:00 2001 From: Vincent Li Date: Thu, 10 Oct 2024 21:25:48 +0000 Subject: [PATCH] xdp-geoip: XDP GeoIP location blocklist XDP GeoIP location blocklist to block IP from user configured Geo location Signed-off-by: Vincent Li --- Makefile | 2 +- xdp-geoip/Makefile | 9 +++ xdp-geoip/xdp_geoip.bpf.c | 74 +++++++++++++++++++++++ xdp-geoip/xdp_geoip.c | 120 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 xdp-geoip/Makefile create mode 100644 xdp-geoip/xdp_geoip.bpf.c create mode 100644 xdp-geoip/xdp_geoip.c diff --git a/Makefile b/Makefile index 980817ec..26691eae 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ include config.mk UTILS := xdp-filter xdp-loader xdp-dump ifneq ($(BPFTOOL),) -UTILS += xdp-bench xdp-monitor xdp-trafficgen xdp-synproxy xdp-dnsrrl xdp-udp xdp-dns xdp-sni +UTILS += xdp-bench xdp-monitor xdp-trafficgen xdp-synproxy xdp-dnsrrl xdp-udp xdp-dns xdp-sni xdp-geoip endif SUBDIRS := lib $(UTILS) diff --git a/xdp-geoip/Makefile b/xdp-geoip/Makefile new file mode 100644 index 00000000..637d824d --- /dev/null +++ b/xdp-geoip/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) + +XDP_TARGETS := xdp_geoip.bpf +BPF_SKEL_TARGETS := $(XDP_TARGETS) +USER_TARGETS := xdp_geoip + +LIB_DIR = ../lib + +include $(LIB_DIR)/common.mk diff --git a/xdp-geoip/xdp_geoip.bpf.c b/xdp-geoip/xdp_geoip.bpf.c new file mode 100644 index 00000000..b1805109 --- /dev/null +++ b/xdp-geoip/xdp_geoip.bpf.c @@ -0,0 +1,74 @@ +/* + * 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 "vmlinux_local.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ipv4_lpm_key { + __u32 prefixlen; + __u32 saddr; +}; + +struct { + __uint(type, BPF_MAP_TYPE_LPM_TRIE); + __uint(max_entries, 1000000); + __uint(pinning, LIBBPF_PIN_BY_NAME); + __uint(map_flags, BPF_F_NO_PREALLOC); + __type(key, struct ipv4_lpm_key); + __type(value, __u8); +} geoip_map SEC(".maps"); + +SEC("xdp") +int xdp_geoip(struct xdp_md *ctx) { + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + struct ethhdr *eth = data; + struct iphdr *iph; + + if ((void *)(eth + 1) > data_end) + return XDP_PASS; + + if (eth->h_proto != __constant_htons(ETH_P_IP)) + return XDP_PASS; + + iph = (struct iphdr *)(eth + 1); + if ((void *)(iph + 1) > data_end) + return XDP_PASS; + + struct ipv4_lpm_key key = { + .prefixlen = 32, + .saddr = iph->saddr, + }; + + __u8 *action = bpf_map_lookup_elem(&geoip_map, &key); + if (action && *action == 1) { + return XDP_DROP; + } + + return XDP_PASS; +} + +char _license[] SEC("license") = "GPL"; + diff --git a/xdp-geoip/xdp_geoip.c b/xdp-geoip/xdp_geoip.c new file mode 100644 index 00000000..cab30321 --- /dev/null +++ b/xdp-geoip/xdp_geoip.c @@ -0,0 +1,120 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#define MAP_PATH "/sys/fs/bpf/xdp-geoip/geoip_map" +#define BATCH_SIZE 1000 // Number of entries per batch + +struct ipv4_lpm_key { + __u32 prefixlen; + __u32 saddr; +}; + +// Structure for country code and action +struct ip_entry { + struct ipv4_lpm_key key; + __u8 action; +}; + +void add_ips_batch(int map_fd, struct ip_entry *entries, size_t count) { + struct ipv4_lpm_key keys[BATCH_SIZE]; + __u8 actions[BATCH_SIZE]; + for (size_t i = 0; i < count; i++) { + keys[i] = entries[i].key; + actions[i] = entries[i].action; + } + + __u32 num_entries = count; + int ret = bpf_map_update_batch(map_fd, keys, actions, &num_entries, NULL); + if (ret) { + fprintf(stderr, "Batch update failed: %s\n", strerror(errno)); + } else { + printf("Batch update successful\n"); + } +} + +int main(int argc, char **argv) { + if (argc < 3) { + printf("Usage: %s \n", argv[0]); + return 1; + } + + char *enabled_country = argv[2]; // Country code provided from web interface + + // Open the BPF map + int map_fd = bpf_obj_get(MAP_PATH); + if (map_fd < 0) { + perror("bpf_obj_get"); + return 1; + } + + FILE *file = fopen(argv[1], "r"); + if (!file) { + perror("fopen"); + return 1; + } + + struct ip_entry entries[BATCH_SIZE]; + size_t count = 0; + + char line[256]; + while (fgets(line, sizeof(line), file)) { + // Look for lines that start with "add" and a two-letter country code + if (strncmp(line, "add ", 4) == 0) { + char *country_code = strtok(line + 4, "v"); // Extract the country code + strtok(NULL, " "); // Skip the next token ("hash") + char *ip_str = strtok(NULL, " /"); // Extract the IP address + char *prefix_str = strtok(NULL, " "); // Extract the prefix length + + // If country code matches the enabled country, add the IP + if (country_code && strcmp(country_code, enabled_country) == 0) { + if (ip_str && prefix_str) { + entries[count].key.prefixlen = atoi(prefix_str); + inet_pton(AF_INET, ip_str, &entries[count].key.saddr); + + // Assuming we block IPs from enabled country + entries[count].action = 1; // Block + + count++; + + if (count == BATCH_SIZE) { + add_ips_batch(map_fd, entries, count); + count = 0; + } + } + } + } + } + + // Process any remaining IPs + if (count > 0) { + add_ips_batch(map_fd, entries, count); + } + + fclose(file); + close(map_fd); + return 0; +} +