Skip to content

Commit

Permalink
added ddos_protect diag mode and icmp ttl logging
Browse files Browse the repository at this point in the history
  • Loading branch information
r-caamano committed Mar 14, 2024
1 parent 81b1272 commit de9ba23
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 17 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

---
# [0.5.12] - 2024-03-14

###

- Added ddos_protect diag mode
- Added icmp unrecheable ttl logging. Inner and outer.

# [0.5.11] - 2024-02-27

###
Expand Down
85 changes: 72 additions & 13 deletions src/zfw.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
#define CLIENT_INITIATED_UDP_SESSION 12
#define ICMP_INNER_IP_HEADER_TOO_BIG 13

bool ddos = false;
bool add = false;
bool delete = false;
bool list = false;
Expand Down Expand Up @@ -137,6 +138,7 @@ const char *tun_map_path = "/sys/fs/bpf/tc/globals/tun_map";
const char *if_tun_map_path = "/sys/fs/bpf/tc/globals/ifindex_tun_map";
const char *transp_map_path = "/sys/fs/bpf/tc/globals/zet_transp_map";
const char *rb_map_path = "/sys/fs/bpf/tc/globals/rb_map";
const char *ddos_map_path = "/sys/fs/bpf/tc/globals/ddos_protect_map";
char doc[] = "zfw -- ebpf firewall configuration tool";
const char *if_map_path;
char *diag_interface;
Expand All @@ -147,12 +149,13 @@ char *ssh_interface;
char *prefix_interface;
char *tun_interface;
char *vrrp_interface;
char *ddos_interface;
char *monitor_interface;
char *tc_interface;
char *log_file_name;
char *object_file;
char *direction_string;
const char *argp_program_version = "0.5.11";
const char *argp_program_version = "0.5.12";
struct ring_buffer *ring_buffer;

__u8 if_list[MAX_IF_LIST_ENTRIES];
Expand Down Expand Up @@ -220,6 +223,7 @@ struct diag_ip4
bool tun_mode;
bool vrrp;
bool eapol;
bool ddos_filtering;
};

struct tproxy_port_mapping
Expand Down Expand Up @@ -428,10 +432,10 @@ void disable_ebpf()
disable = true;
tc = true;
interface_tc();
const char *maps[11] = {tproxy_map_path, diag_map_path, if_map_path, count_map_path,
const char *maps[12] = {tproxy_map_path, diag_map_path, if_map_path, count_map_path,
udp_map_path, matched_map_path, tcp_map_path, tun_map_path, if_tun_map_path,
transp_map_path, rb_map_path};
for (int map_count = 0; map_count < 11; map_count++)
transp_map_path, rb_map_path, ddos_map_path};
for (int map_count = 0; map_count < 12; map_count++)
{

int stat = remove(maps[map_count]);
Expand Down Expand Up @@ -989,6 +993,18 @@ bool set_diag(uint32_t *idx)
}
printf("Set vrrp mode to %d for %s\n", !disable, vrrp_interface);
}
if (ddos)
{
if (!disable)
{
o_diag.ddos_filtering = true;
}
else
{
o_diag.ddos_filtering = false;
}
printf("Set ddos detect to %d for %s\n", !disable, ddos_interface);
}
int ret = syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM, &diag_map, sizeof(diag_map));
if (ret)
{
Expand Down Expand Up @@ -1017,6 +1033,7 @@ bool set_diag(uint32_t *idx)
printf("%-24s:%d\n", "tun mode intercept", o_diag.tun_mode);
printf("%-24s:%d\n", "vrrp enable", o_diag.vrrp);
printf("%-24s:%d\n", "eapol enable", o_diag.eapol);
printf("%-24s:%d\n", "ddos filtering", o_diag.ddos_filtering);
printf("--------------------------\n\n");
}
return true;
Expand Down Expand Up @@ -1163,8 +1180,9 @@ void interface_diag()
tun_interface = address->ifa_name;
vrrp_interface = address->ifa_name;
eapol_interface = address->ifa_name;
ddos_interface = address->ifa_name;
}
if(!strncmp(address->ifa_name, "ziti", 4) && (tun || per_interface || ssh_disable || echo || vrrp || eapol)){
if(!strncmp(address->ifa_name, "ziti", 4) && (tun || per_interface || ssh_disable || echo || vrrp || eapol || ddos)){
if(per_interface && !strncmp(prefix_interface, "ziti", 4)){
printf("%s:zfw does not allow setting on tun interfaces!\n", address->ifa_name);
}
Expand All @@ -1183,6 +1201,9 @@ void interface_diag()
if(eapol && !strncmp(eapol_interface, "ziti", 4)){
printf("%s:zfw does not allow setting on tun interfaces!\n", address->ifa_name);
}
if(ddos && !strncmp(ddos_interface, "ziti", 4)){
printf("%s:zfw does not allow setting on tun interfaces!\n", address->ifa_name);
}
address = address->ifa_next;
continue;
}
Expand All @@ -1202,6 +1223,14 @@ void interface_diag()
}
}

if (ddos)
{
if (!strcmp(ddos_interface, address->ifa_name))
{
set_diag(&idx);
}
}

if (eapol)
{
if (!strcmp(eapol_interface, address->ifa_name))
Expand Down Expand Up @@ -1779,7 +1808,10 @@ static int process_events(void *ctx, void *data, size_t len){
}
else if(evt->proto == IPPROTO_ICMP && ifname){
__u16 code = evt->tracking_code;
__u8 inner_ttl = evt->dest[0];
__u8 outer_ttl = evt->source[0];
if(code == 4){
/*evt->sport is use repurposed store next hop mtu*/
sprintf(message, "%s : %s : %s : %s :%s --> reported next hop mtu:%d > FRAGMENTATION NEEDED IN PATH TO:%s:%d\n", ts, ifname,
(evt->direction == INGRESS) ? "INGRESS" : "EGRESS", protocol,saddr, ntohs(evt->sport), daddr, ntohs(evt->dport));
if(logging){
Expand All @@ -1790,8 +1822,7 @@ static int process_events(void *ctx, void *data, size_t len){
}else{
char *code_string = NULL;
char *protocol_string = NULL;
/*evt->sport is use repurposed store encapsulated higher layer protocol*/
if(evt->sport == IPPROTO_TCP){
if(evt->dest[1] == IPPROTO_TCP){
protocol_string = "TCP";
}else{
protocol_string = "UDP";
Expand All @@ -1810,8 +1841,8 @@ static int process_events(void *ctx, void *data, size_t len){
}

if(code_string){
sprintf(message, "%s : %s : %s : %s :%s --> REPORTED:%s > in PATH TO:%s:%s:%d\n", ts, ifname,
(evt->direction == INGRESS) ? "INGRESS" : "EGRESS", protocol,saddr, code_string, daddr, protocol_string, ntohs(evt->dport));
sprintf(message, "%s : %s : %s : %s :%s --> REPORTED:%s > in PATH TO:%s:%s:%d OUTER-TTL:%d INNER-TTL:%d\n", ts, ifname,
(evt->direction == INGRESS) ? "INGRESS" : "EGRESS", protocol,saddr, code_string, daddr, protocol_string, ntohs(evt->dport), outer_ttl, inner_ttl);
if(logging){
res = write_log(log_file_name, message);
}else{
Expand Down Expand Up @@ -2441,6 +2472,7 @@ static struct argp_option options[] = {
{"verbose", 'v', "", 0, "Enable verbose tracing on interface", 0},
{"enable-eapol", 'w', "", 0, "enable 802.1X eapol packets inbound on interface", 0},
{"disable-ssh", 'x', "", 0, "Disable inbound ssh to interface (default enabled)", 0},
{"ddos-filtering", 'a', "", 0, "Manually enable/disable ddos filtering on interface", 0},
{"direction", 'z', "", 0, "Set direction", 0},
{0}};

Expand Down Expand Up @@ -2626,6 +2658,28 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state)
tc_interface = arg;
}
break;
case 'a':
if (!strlen(arg) || (strchr(arg, '-') != NULL))
{
fprintf(stderr, "Interface name or all required as arg to -a, --ddos-filter: %s\n", arg);
fprintf(stderr, "%s --help for more info\n", program_name);
exit(1);
}
idx = if_nametoindex(arg);
if(strcmp("all", arg) && idx == 0){
printf("Interface not found: %s\n", arg);
exit(1);
}
ddos = true;
if (!strcmp("all", arg))
{
all_interface = true;
}
else
{
ddos_interface = arg;
}
break;
case 'c':
if (!inet_aton(arg, &dcidr))
{
Expand Down Expand Up @@ -2922,15 +2976,15 @@ int main(int argc, char **argv)
if (logging)
{
if ((tcfilter || echo || ssh_disable || verbose || per_interface
|| add || delete || list || flush || eapol) || (!monitor))
|| add || delete || list || flush || eapol) || ddos || vrrp || (!monitor))
{
usage("W, --write-log can only be used in combination call to -M, --monitor");
}
}

if (ebpf_disable)
{
if (tcfilter || echo || ssh_disable || verbose || per_interface || add || delete || list || flush || monitor || eapol)
if (tcfilter || echo || ssh_disable || verbose || per_interface || add || delete || list || flush || monitor || eapol || vrrp || ddos)
{
usage("Q, --disable-ebpf cannot be used in combination call");
}
Expand All @@ -2957,6 +3011,11 @@ int main(int argc, char **argv)
usage("-T, --set-tun-mode cannot be set as a part of combination call to zfw");
}

if ((ddos && (monitor || tun || echo || ssh_disable || verbose || per_interface || add || delete || list || flush || tcfilter || vrrp || eapol)))
{
usage("-a, --ddsos-filter cannot be set as a part of combination call to zfw");
}

if ((eapol && (monitor || tun || echo || ssh_disable || verbose || per_interface || add || delete || list || flush || tcfilter || vrrp)))
{
usage("-M, --enable-eapol cannot be set as a part of combination call to zfw");
Expand Down Expand Up @@ -3007,7 +3066,7 @@ int main(int argc, char **argv)
usage("Missing argument -r, --route requires -I --insert, -D --delete or -F --flush");
}

if (disable && (!ssh_disable && !echo && !verbose && !per_interface && !tcfilter && !tun && !vrrp && !eapol))
if (disable && (!ssh_disable && !echo && !verbose && !per_interface && !tcfilter && !tun && !vrrp && !eapol && !ddos))
{
usage("Missing argument at least one of -e, -v, -x, w, or -E, -P, -R, -T, -X");
}
Expand Down Expand Up @@ -3160,7 +3219,7 @@ int main(int argc, char **argv)
map_list();
}
}
else if (vrrp || verbose || ssh_disable || echo || per_interface || tun || eapol)
else if (vrrp || verbose || ssh_disable || echo || per_interface || tun || eapol || ddos)
{
interface_diag();
exit(0);
Expand Down
40 changes: 36 additions & 4 deletions src/zfw_tc_ingress.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ struct diag_ip4 {
bool tun_mode;
bool vrrp;
bool eapol;
bool ddos_filtering;
};

/*Value to tun_map*/
Expand Down Expand Up @@ -236,6 +237,15 @@ struct {
__uint(map_flags, BPF_F_NO_PREALLOC);
} zet_transp_map SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(key_size, sizeof(uint32_t));
__uint(value_size,sizeof(bool));
__uint(max_entries, BPF_MAX_ENTRIES);
__uint(pinning, LIBBPF_PIN_BY_NAME);
__uint(map_flags, BPF_F_NO_PREALLOC);
} ddos_protect_map SEC(".maps");

/*map to track up to 3 key matches per incoming packet search. Map is
then used to search for port mappings. This was required when source filtering was
added to accommodate the additional instructions per ebpf program. The search now spans
Expand Down Expand Up @@ -458,6 +468,13 @@ static inline void clear_match_tracker(struct match_key key){
bpf_map_delete_elem(&matched_map, &key);
}

/*Function to check if ip is in ddos whitelist*/
static inline bool *get_ddos_list(unsigned int key){
bool *match;
match = bpf_map_lookup_elem(&ddos_protect_map, &key);
return match;
}

static inline void send_event(struct bpf_event *new_event){
struct bpf_event *rb_event;
rb_event = bpf_ringbuf_reserve(&rb_map, sizeof(*rb_event), 0);
Expand Down Expand Up @@ -744,6 +761,9 @@ int bpf_sk_splice(struct __sk_buff *skb){
return TC_ACT_SHOT;
}
if((inner_iph->protocol == IPPROTO_TCP) || ((inner_iph->protocol == IPPROTO_UDP))){
event.source[0] = iph->ttl;
event.dest[0] = inner_iph->ttl;
event.dest[1] = inner_iph->protocol;
struct bpf_sock_tuple *o_session = (struct bpf_sock_tuple *)(void*)(long)&inner_iph->saddr;
if ((unsigned long)(o_session + 1) > (unsigned long)skb->data_end){
event.error_code = IP_TUPLE_TOO_BIG;
Expand All @@ -760,8 +780,6 @@ int bpf_sk_splice(struct __sk_buff *skb){
event.tracking_code = icmph->code;
if(icmph->code == 4){
event.sport = icmph->un.frag.mtu;
}else{
event.sport = inner_iph->protocol;
}
event.dport = o_session->ipv4.dport;
send_event(&event);
Expand Down Expand Up @@ -1405,7 +1423,14 @@ int bpf_sk_splice5(struct __sk_buff *skb){
sockcheck.ipv4.dport = tproxy->port_mapping[port_key].tproxy_port;
if(!local_diag->per_interface){
if(tproxy->port_mapping[port_key].tproxy_port == 0){
return TC_ACT_OK;
if(!local_diag->ddos_filtering){
return TC_ACT_OK;
}
else{
if(get_ddos_list(tuple->ipv4.saddr)){
return TC_ACT_OK;
}
}
}
if(!local_diag->tun_mode){
sk = get_sk(key, skb, sockcheck);
Expand Down Expand Up @@ -1454,7 +1479,14 @@ int bpf_sk_splice5(struct __sk_buff *skb){
for(int x = 0; x < MAX_IF_LIST_ENTRIES; x++){
if(tproxy->port_mapping[port_key].if_list[x] == skb->ifindex){
if(tproxy->port_mapping[port_key].tproxy_port == 0){
return TC_ACT_OK;
if(!local_diag->ddos_filtering){
return TC_ACT_OK;
}
else{
if(get_ddos_list(tuple->ipv4.saddr)){
return TC_ACT_OK;
}
}
}
if(!local_diag->tun_mode){
sk = get_sk(key, skb, sockcheck);
Expand Down
1 change: 1 addition & 0 deletions src/zfw_tc_outbound_track.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ struct diag_ip4 {
bool tun_mode;
bool vrrp;
bool eapol;
bool ddos_filtering;
};

//map to keep status of diagnostic rules
Expand Down

0 comments on commit de9ba23

Please sign in to comment.