From de9ba23bebee3393a5d98c1db803788cb18284a0 Mon Sep 17 00:00:00 2001 From: r-caamano Date: Thu, 14 Mar 2024 12:44:48 +0000 Subject: [PATCH 1/2] added ddos_protect diag mode and icmp ttl logging --- CHANGELOG.md | 7 +++ src/zfw.c | 85 +++++++++++++++++++++++++++++++------ src/zfw_tc_ingress.c | 40 +++++++++++++++-- src/zfw_tc_outbound_track.c | 1 + 4 files changed, 116 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2dab79..f11d414 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 ### diff --git a/src/zfw.c b/src/zfw.c index 6fed98e..1ab3c4f 100644 --- a/src/zfw.c +++ b/src/zfw.c @@ -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; @@ -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; @@ -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]; @@ -220,6 +223,7 @@ struct diag_ip4 bool tun_mode; bool vrrp; bool eapol; + bool ddos_filtering; }; struct tproxy_port_mapping @@ -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]); @@ -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) { @@ -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; @@ -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); } @@ -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; } @@ -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)) @@ -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){ @@ -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"; @@ -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{ @@ -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}}; @@ -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)) { @@ -2922,7 +2976,7 @@ 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"); } @@ -2930,7 +2984,7 @@ int main(int argc, char **argv) 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"); } @@ -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"); @@ -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"); } @@ -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); diff --git a/src/zfw_tc_ingress.c b/src/zfw_tc_ingress.c index 92f467d..1cd0670 100644 --- a/src/zfw_tc_ingress.c +++ b/src/zfw_tc_ingress.c @@ -201,6 +201,7 @@ struct diag_ip4 { bool tun_mode; bool vrrp; bool eapol; + bool ddos_filtering; }; /*Value to tun_map*/ @@ -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 @@ -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); @@ -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; @@ -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); @@ -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); @@ -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); diff --git a/src/zfw_tc_outbound_track.c b/src/zfw_tc_outbound_track.c index f8824c4..34f11b4 100644 --- a/src/zfw_tc_outbound_track.c +++ b/src/zfw_tc_outbound_track.c @@ -99,6 +99,7 @@ struct diag_ip4 { bool tun_mode; bool vrrp; bool eapol; + bool ddos_filtering; }; //map to keep status of diagnostic rules From f606957f2ed61fa392e768e678d33c1c2f6edc74 Mon Sep 17 00:00:00 2001 From: r-caamano Date: Thu, 14 Mar 2024 12:58:07 +0000 Subject: [PATCH 2/2] Fixed typo in BUILD.md referencing parent repo --- BUILD.md | 4 ++-- CHANGELOG.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/BUILD.md b/BUILD.md index c94e2b9..1dbd217 100644 --- a/BUILD.md +++ b/BUILD.md @@ -17,7 +17,7 @@ ```bash mkdir ~/repos cd repos - git clone https://github.com/r-caamano/zfw.git + git clone https://github.com/netfoundry/zfw.git cd zfw/src make all sudo make install ARGS= @@ -40,7 +40,7 @@ ```bash mkdir ~/repos cd repos - git clone https://github.com/r-caamano/zfw.git + git clone https://github.com/netfoundry/zfw.git cd zfw/src make all sudo make install ARGS= diff --git a/CHANGELOG.md b/CHANGELOG.md index f11d414..fae0ded 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file. The format - Added ddos_protect diag mode - Added icmp unrecheable ttl logging. Inner and outer. +- Fixed BUILD.md incorrect reference to parent repo # [0.5.11] - 2024-02-27