Skip to content

Commit

Permalink
Merge pull request #12 from gospo/master
Browse files Browse the repository at this point in the history
samples/bpf: xdp_ddos01 add support for port blacklisting
  • Loading branch information
netoptimizer committed Mar 28, 2017
2 parents 865e062 + 888606b commit 329aaaa
Show file tree
Hide file tree
Showing 4 changed files with 272 additions and 9 deletions.
82 changes: 76 additions & 6 deletions kernel/samples/bpf/xdp_ddos01_blacklist_cmdline.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* Copyright(c) 2017 Jesper Dangaard Brouer, Red Hat, Inc.
Copyright(c) 2017 Andy Gospodarek, Broadcom Limited, Inc.
*/
static const char *__doc__=
" XDP ddos01: command line tool";
Expand All @@ -12,6 +13,7 @@ static const char *__doc__=
#include <string.h>
#include <unistd.h>
#include <locale.h>
#include <linux/bitops.h>

#include <sys/resource.h>
#include <getopt.h>
Expand All @@ -33,6 +35,8 @@ static const struct option long_options[] = {
{"stats", no_argument, NULL, 's' },
{"sec", required_argument, NULL, 's' },
{"list", no_argument, NULL, 'l' },
{"udp-dport", required_argument, NULL, 'u' },
{"tcp-dport", required_argument, NULL, 't' },
{0, 0, NULL, 0 }
};

Expand All @@ -44,6 +48,15 @@ static const char *xdp_action_names[XDP_ACTION_MAX] = {
[XDP_PASS] = "XDP_PASS",
[XDP_TX] = "XDP_TX",
};

#define FILTER_TCP 0
#define FILTER_UDP 1
#define XDP_PROTO_FILTER_MAX 2
static const char *xdp_proto_filter_names[XDP_PROTO_FILTER_MAX] = {
[FILTER_TCP] = "TCP",
[FILTER_UDP] = "UDP",
};

static const char *action2str(int action)
{
if (action < XDP_ACTION_MAX)
Expand Down Expand Up @@ -197,21 +210,51 @@ static void blacklist_print_ip(__u32 ip, __u64 count)
printf("\"%s\" : %llu\n", ip_txt, count);
}

static void blacklist_print_port(int key, u32 val, __u64 count)
{
int i;
printf(" %d: ", key);
for (i = 0; i < XDP_PROTO_FILTER_MAX; i++)
if (val & (1 << i))
printf("%s ",xdp_proto_filter_names[i]);
printf(": %llu\n", count);
}

static void blacklist_list_all(int fd)
{
__u32 key = 0, next_key;
__u64 value;

printf("{\n");
while (bpf_map_get_next_key(fd, &key, &next_key) == 0) {
printf("%s", key ? "," : " ");
key = next_key;
value = get_key32_value64_percpu(fd, key);
blacklist_print_ip(key, value);
}
printf("}\n");
}

static void blacklist_list_all_ports(int portfd, int countfd)
{
__u32 key = 0, next_key;
__u64 value;
__u64 count;

printf("{\n");
while (bpf_map_get_next_key(portfd, &key, &next_key) == 0) {
if ((bpf_map_lookup_elem(portfd, &key, &value)) != 0) {
fprintf(stderr,
"ERR: bpf_map_lookup_elem(%d) failed key:0x%X\n", portfd, key);
}
count = get_key32_value64_percpu(countfd, key);
if (value)
blacklist_print_port(key, value, count);

key = next_key;
}
printf("}\n");
}

int main(int argc, char **argv)
{
# define STR_MAX 42 /* For trivial input validation */
Expand All @@ -223,13 +266,17 @@ int main(int argc, char **argv)
int interval = 1;
int fd_blacklist;
int fd_verdict;
int fd_port_blacklist;
int fd_port_blacklist_count;
int longindex = 0;
bool do_list = false;
int opt;
int dport = 0;
int proto = IPPROTO_TCP;

fd_verdict = open_bpf_map(file_verdict);

while ((opt = getopt_long(argc, argv, "adshi:",
while ((opt = getopt_long(argc, argv, "adshi:t:u:",
long_options, &longindex)) != -1) {
switch (opt) {
case 'a':
Expand All @@ -246,6 +293,12 @@ int main(int argc, char **argv)
ip_string = (char *)&_ip_string_buf;
strncpy(ip_string, optarg, STR_MAX);
break;
case 'u':
proto = IPPROTO_UDP;
case 't':
if (optarg)
dport = atoi(optarg);
break;
case 's': /* shared: --stats && --sec */
stats = true;
if (optarg)
Expand All @@ -266,14 +319,25 @@ int main(int argc, char **argv)
if (action) {
int res = 0;

if (!ip_string) {
if (!ip_string && !dport) {
fprintf(stderr,
"ERR: action require type+data, e.g option --ip\n");
goto fail_opt;
}
fd_blacklist = open_bpf_map(file_blacklist);
res = blacklist_modify(fd_blacklist, ip_string, action);
close(fd_blacklist);

if (ip_string) {
fd_blacklist = open_bpf_map(file_blacklist);
res = blacklist_modify(fd_blacklist, ip_string, action);
close(fd_blacklist);
}

if (dport) {
fd_port_blacklist = open_bpf_map(file_port_blacklist);
fd_port_blacklist_count = open_bpf_map(file_port_blacklist_count);
res = blacklist_port_modify(fd_port_blacklist, fd_port_blacklist_count, dport, action, proto);
close(fd_port_blacklist);
close(fd_port_blacklist_count);
}
return res;
}

Expand All @@ -288,6 +352,12 @@ int main(int argc, char **argv)
fd_blacklist = open_bpf_map(file_blacklist);
blacklist_list_all(fd_blacklist);
close(fd_blacklist);

fd_port_blacklist = open_bpf_map(file_port_blacklist);
fd_port_blacklist_count = open_bpf_map(file_port_blacklist_count);
blacklist_list_all_ports(fd_port_blacklist, fd_port_blacklist_count);
close(fd_port_blacklist);
close(fd_port_blacklist_count);
}

/* Show statistics by polling */
Expand Down
99 changes: 99 additions & 0 deletions kernel/samples/bpf/xdp_ddos01_blacklist_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#define EXIT_FAIL_MAP_FILE 22
#define EXIT_FAIL_MAP_FS 23
#define EXIT_FAIL_IP 30
#define EXIT_FAIL_PORT 31
#define EXIT_FAIL_BPF 40
#define EXIT_FAIL_BPF_ELF 41
#define EXIT_FAIL_BPF_RELOCATE 42
Expand All @@ -25,6 +26,11 @@ static int verbose = 1;
*/
static const char *file_blacklist = "/sys/fs/bpf/ddos_blacklist";
static const char *file_verdict = "/sys/fs/bpf/ddos_blacklist_stat_verdict";

static const char *file_port_blacklist = "/sys/fs/bpf/ddos_port_blacklist";
static const char *file_port_blacklist_count = "/sys/fs/bpf/ddos_port_blacklist_count";


// TODO: create subdir per ifname, to allow more XDP progs

/* gettime returns the current time of day in nanoseconds.
Expand All @@ -49,6 +55,11 @@ uint64_t gettime(void)
#define ACTION_ADD (1 << 0)
#define ACTION_DEL (1 << 1)

enum {
DDOS_FILTER_TCP = 0,
DDOS_FILTER_UDP,
};

static int blacklist_modify(int fd, char *ip_string, unsigned int action)
{
unsigned int nr_cpus = bpf_num_possible_cpus();
Expand Down Expand Up @@ -98,4 +109,92 @@ static int blacklist_modify(int fd, char *ip_string, unsigned int action)
return EXIT_OK;
}

static int blacklist_port_modify(int fd, int countfd, int dport, unsigned int action, int proto)
{
unsigned int nr_cpus = bpf_num_possible_cpus();
__u64 curr_values[nr_cpus];
__u64 stat_values[nr_cpus];
__u64 value;
__u32 key = dport;
int res;
int i;

if (action != ACTION_ADD && action != ACTION_DEL)
{
fprintf(stderr, "ERR: %s() invalid action 0x%x\n",
__func__, action);
return EXIT_FAIL_OPTION;
}

if (proto == IPPROTO_TCP)
value = 1 << DDOS_FILTER_TCP;
else if (proto == IPPROTO_UDP)
value = 1 << DDOS_FILTER_UDP;
else {
fprintf(stderr, "ERR: %s() invalid action 0x%x\n",
__func__, action);
return EXIT_FAIL_OPTION;
}

memset(curr_values, 0, sizeof(__u64) * nr_cpus);

if (dport > 65535) {
fprintf(stderr,
"ERR: destination port \"%d\" invalid\n",
dport);
return EXIT_FAIL_PORT;
}

if (bpf_map_lookup_elem(fd, &key, curr_values)) {
fprintf(stderr,
"%s() 1 bpf_map_lookup_elem(key:0x%X) failed errno(%d/%s)",
__func__, key, errno, strerror(errno));
}

if (action == ACTION_ADD) {
/* add action set bit */
for (i=0; i<nr_cpus; i++)
curr_values[i] |= value;
} else if (action == ACTION_DEL) {
/* delete action clears bit */
for (i=0; i<nr_cpus; i++)
curr_values[i] &= ~(value);
}

res = bpf_map_update_elem(fd, &key, &curr_values, BPF_EXIST);

if (res != 0) { /* 0 == success */
fprintf(stderr,
"%s() dport:%d key:0x%X value errno(%d/%s)",
__func__, dport, key, errno, strerror(errno));

if (errno == 17) {
fprintf(stderr, ": Port already in blacklist\n");
return EXIT_OK;
}
fprintf(stderr, "\n");
return EXIT_FAIL_MAP_KEY;
}

if (action == ACTION_DEL) {
/* clear stats on delete */
memset(stat_values, 0, sizeof(__u64) * nr_cpus);
res = bpf_map_update_elem(countfd, &key, &stat_values, BPF_EXIST);

if (res != 0) { /* 0 == success */
fprintf(stderr,
"%s() dport:%d key:0x%X value errno(%d/%s)",
__func__, dport, key, errno, strerror(errno));

fprintf(stderr, "\n");
return EXIT_FAIL_MAP_KEY;
}
}

if (verbose)
fprintf(stderr,
"%s() dport:%d key:0x%X\n", __func__, dport, key);
return EXIT_OK;
}

#endif

0 comments on commit 329aaaa

Please sign in to comment.