diff --git a/CHANGELOG.md b/CHANGELOG.md index b5aecc3..26a01ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ 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.8.10] - 2024-07-29 + +- Updated start_ebpf_controller.py to only clear ingress filters on restart and also removed ```-r, --route``` from the flush. +- Added native masquerade for IPv4/IPv6 passthrough connections. + ### # [0.8.9] - 2024-07-28 diff --git a/README.md b/README.md index 82aacdb..24d8e77 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,19 @@ --- This firewall application utilizes both tc-ebpf and xdp to provide stateful firewalling for an [OpenZiti](https://docs.openziti.io/) ziti-edge-tunnel installation and is meant as a replacement for packet -filtering. It can be used in conjunction with ufw's masquerade feature on a WAN facing interface if -the zfw_outbound_track.o is activated in the egress direction. It can also be used in conjunction with OpenZiti -edge-routers. +filtering. It can also be used in conjunction with OpenZiti edge-routers or as a standalone fw. It now has built in +EBPF based masquerade capability for both IPv4/IPv6. ## New features in 0.8.x - +### Native EBPF based IPv4 and IPv6 Masquerade support + +zfw can now provide native IPv4/IPv6 masquerade operation for outbound pass through connections which can be enabled via: + +```sudo zfw -k, --masquerade ``` + +This function requires that both ingress and egress TC filters are enabled on outbound interface. + ### Explicit Deny Rules This feature adds the ability to enter explicit deny rules by appending ```-d, --disable to the -I, --insert rule`` to both ingress and egress rules. Rule precedence is based on longest match prefix. If the prefix is the same then the precedence follows the order entry of the rules, which when listed will go from top to bottom for ports with in the same prefix e.g. diff --git a/files/scripts/start_ebpf_controller.py b/files/scripts/start_ebpf_controller.py index 2371358..9dd7235 100755 --- a/files/scripts/start_ebpf_controller.py +++ b/files/scripts/start_ebpf_controller.py @@ -432,7 +432,7 @@ def set_local_rules(ip): os.system("/opt/openziti/bin/user/user_rules.sh") else: print("ebpf already running!"); - os.system("/usr/sbin/zfw -F -r") + os.system("/usr/sbin/zfw -F -z ingress") print("Flushed Table") for i in internal_list: if(not tc_status(i, "ingress")): diff --git a/src/zfw.c b/src/zfw.c index dcfabe3..f696968 100644 --- a/src/zfw.c +++ b/src/zfw.c @@ -109,6 +109,7 @@ bool prot = false; bool route = false; bool passthru = false; bool intercept = false; +bool masquerade = false; bool echo = false; bool eapol = false; bool verbose = false; @@ -213,7 +214,7 @@ const char *egress_map_path = "/sys/fs/bpf/tc/globals/zt_egress_map"; const char *egress6_map_path = "/sys/fs/bpf/tc/globals/zt_egress6_map"; const char *egress_count_map_path = "/sys/fs/bpf/tc/globals/egress_count_map"; const char *egress_count6_map_path = "/sys/fs/bpf/tc/globals/egress6_count_map"; - +const char *masquerade_map_path = "/sys/fs/bpf/tc/globals/masquerade_map"; char doc[] = "zfw -- ebpf firewall configuration tool"; const char *if_map_path; char *diag_interface; @@ -232,9 +233,10 @@ char *tc_interface; char *log_file_name; char *object_file; char *direction_string; +char *masq_interface; char check_alt[IF_NAMESIZE]; -const char *argp_program_version = "0.8.9"; +const char *argp_program_version = "0.8.10"; struct ring_buffer *ring_buffer; __u32 if_list[MAX_IF_LIST_ENTRIES]; @@ -367,8 +369,8 @@ struct bpf_event unsigned char dest[6]; }; -struct diag_ip4 -{ +/*value to diag_map*/ +struct diag_ip4 { bool echo; bool verbose; bool per_interface; @@ -381,6 +383,7 @@ struct diag_ip4 bool ddos_filtering; bool ipv6_enable; bool outbound_filter; + bool masquerade; }; struct tproxy_tuple @@ -648,14 +651,14 @@ void disable_ebpf() disable = true; tc = true; interface_tc(); - const char *maps[33] = {tproxy_map_path, diag_map_path, if_map_path, count_map_path, + const char *maps[34] = {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, ddos_saddr_map_path, ddos_dport_map_path, syn_count_map_path, tp_ext_map_path, if_list_ext_map_path, range_map_path, wildcard_port_map_path, tproxy6_map_path, if6_map_path, count6_map_path, matched6_map_path, egress_range_map_path, egress_if_list_ext_map_path, egress_ext_map_path, egress_map_path, egress6_map_path, egress_count_map_path, egress_count6_map_path, - egress_matched6_map_path, egress_matched_map_path, udp_ingress_map_path, tcp_ingress_map_path}; - for (int map_count = 0; map_count < 33; map_count++) + egress_matched6_map_path, egress_matched_map_path, udp_ingress_map_path, tcp_ingress_map_path, masquerade_map_path}; + for (int map_count = 0; map_count < 34; map_count++) { int stat = remove(maps[map_count]); @@ -1680,6 +1683,31 @@ bool set_diag(uint32_t *idx) printf("Set disable_ssh is always set to 0 for lo\n"); } } + if (masquerade) + { + if (!disable && *idx != 1) + { + if(!check_filter(*idx,"egress")){ + o_diag.masquerade = true; + }else{ + printf("masquerade not set, no egress filter exists for %s\n", masq_interface); + printf("set first with: sudo zfw -X %s -O /opt/openziti/bin/zfw_tc_outbound_track.o -z egress\n", masq_interface); + close_maps(1); + } + } + else + { + o_diag.masquerade = false; + } + if (*idx != 1) + { + printf("Set masquerade to %d for %s\n", !disable, masq_interface); + } + else + { + printf("Masquerade is always set to 0 for lo\n"); + } + } if (outbound) { if (!disable && *idx != 1) @@ -1687,7 +1715,7 @@ bool set_diag(uint32_t *idx) if(!check_filter(*idx,"egress")){ o_diag.outbound_filter = true; }else{ - printf("outbound filter not set no egress filter exists for %s\n", outbound_interface); + printf("outbound filter not set, no egress filter exists for %s\n", outbound_interface); printf("set first with: sudo zfw -X %s -O /opt/openziti/bin/zfw_tc_outbound_track.o -z egress\n", outbound_interface); close_maps(1); } @@ -1795,6 +1823,7 @@ bool set_diag(uint32_t *idx) 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("%-24s:%d\n", "masquerade", o_diag.masquerade); if (*idx != 1) { printf("%-24s:%d\n", "ipv6 enable", o_diag.ipv6_enable); @@ -1897,7 +1926,9 @@ void interface_tc() set_tc_filter("del"); if(egress){ outbound = true; + masquerade = true; outbound_interface = tc_interface; + masq_interface = tc_interface; } if (diag_fd == -1) { @@ -1968,8 +1999,9 @@ void interface_diag() ddos_interface = address->ifa_name; ipv6_interface = address->ifa_name; outbound_interface = address->ifa_name; + masq_interface = address->ifa_name; } - if (!strncmp(address->ifa_name, "ziti", 4) && (tun || per_interface || ssh_disable || echo || vrrp || eapol || ddos || v6 || outbound)) + if (!strncmp(address->ifa_name, "ziti", 4) && (tun || per_interface || ssh_disable || echo || vrrp || eapol || ddos || v6 || outbound || masquerade)) { if (per_interface && !strncmp(prefix_interface, "ziti", 4)) { @@ -1999,6 +2031,10 @@ void interface_diag() { printf("%s:zfw does not allow setting on ziti tun interfaces!\n", address->ifa_name); } + if (masquerade && !strncmp(masq_interface, "ziti", 4)) + { + printf("%s:zfw does not allow setting on ziti tun interfaces!\n", address->ifa_name); + } if (v6 && !strncmp(ipv6_interface, "ziti", 4)) { printf("%s:zfw does not allow setting on ziti tun interfaces!\n", address->ifa_name); @@ -2017,7 +2053,13 @@ void interface_diag() set_diag(&idx); } } - + if (masquerade) + { + if (!strcmp(masq_interface, address->ifa_name)) + { + set_diag(&idx); + } + } if (outbound) { if (!strcmp(outbound_interface, address->ifa_name)) @@ -5473,6 +5515,7 @@ static struct argp_option options[] = { {"passthrough", 'f', NULL, 0, "List passthrough rules ", 0}, {"high-port", 'h', "", 0, "Set high-port value (1-65535)> ", 0}, {"intercepts", 'i', NULL, 0, "List intercept rules ", 0}, + {"masquerade", 'k', "", 0, "enable outbound masquerade", 0}, {"low-port", 'l', "", 0, "Set low-port value (1-65535)> ", 0}, {"dprefix-len", 'm', "", 0, "Set dest prefix length (1-32) ", 0}, {"oprefix-len", 'n', "", 0, "Set origin prefix length (1-32) ", 0}, @@ -5844,6 +5887,33 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) case 'i': intercept = true; break; + case 'k': + if (!strlen(arg) || (strchr(arg, '-') != NULL)) + { + fprintf(stderr, "Interface name or all required as arg to -k, --masquerade: %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); + } + masquerade = true; + if (!strcmp("all", arg)) + { + all_interface = true; + } + else + { + if(if_indextoname(idx, check_alt)){ + masq_interface = check_alt; + }else{ + masq_interface = arg; + } + } + break; case 'l': low_port = port2s(arg); lpt = true; @@ -6346,6 +6416,14 @@ int main(int argc, char **argv) usage("-X, --set-tc-filter requires -z, --direction for add operation"); } + if (masquerade) + { + if ((dsip || tcfilter || echo || ssh_disable || verbose || per_interface || add || delete || flush || eapol) || ddos || vrrp || monitor || logging || ddport) + { + usage("-k, --masquerade can not be used in combination call"); + } + } + if (v6) { if ((dsip || tcfilter || echo || ssh_disable || verbose || per_interface || add || delete || flush || eapol) || ddos || vrrp || monitor || logging || ddport) @@ -6477,9 +6555,9 @@ int main(int argc, char **argv) } if (disable && (!ssh_disable && !echo && !verbose && !per_interface && !tcfilter && !tun && !vrrp - && !eapol && !ddos && !dsip && !ddport && !v6 && !outbound && !add && !delete)) + && !eapol && !ddos && !dsip && !ddport && !v6 && !outbound && !add && !delete && !masquerade)) { - usage("Missing argument at least one of -a,-b,-6,-e, -u, -v, -w, -x, -y, or -E, -P, -R, -T, -X"); + usage("Missing argument at least one of -a,-b,-6,-e, -k, -u, -v, -w, -x, -y, or -E, -P, -R, -T, -X"); } if (direction && (!tcfilter && !list && !flush && !delete && !add)) @@ -6745,7 +6823,7 @@ int main(int argc, char **argv) } } } - else if (vrrp || verbose || ssh_disable || echo || per_interface || tun || eapol || ddos || v6 || outbound) + else if (vrrp || verbose || ssh_disable || echo || per_interface || tun || eapol || ddos || v6 || outbound || masquerade) { interface_diag(); } diff --git a/src/zfw_monitor.c b/src/zfw_monitor.c index 8cdc125..ef448f5 100644 --- a/src/zfw_monitor.c +++ b/src/zfw_monitor.c @@ -78,7 +78,7 @@ char check_alt[IF_NAMESIZE]; char doc[] = "zfw_monitor -- ebpf firewall monitor tool"; const char *rb_map_path = "/sys/fs/bpf/tc/globals/rb_map"; const char *tproxy_map_path = "/sys/fs/bpf/tc/globals/zt_tproxy_map"; -const char *argp_program_version = "0.8.8"; +const char *argp_program_version = "0.8.10"; union bpf_attr rb_map; int rb_fd = -1; diff --git a/src/zfw_tc_ingress.c b/src/zfw_tc_ingress.c index 33511d3..d4c94be 100644 --- a/src/zfw_tc_ingress.c +++ b/src/zfw_tc_ingress.c @@ -183,6 +183,26 @@ struct tuple_key { __u16 dport; }; +/*Key to masquerade_map*/ +struct masq_key { + uint32_t ifindex; + union { + __u32 ip; + __u32 ip6[4]; + }__in46_u_dest; + __u8 protocol; + __u16 sport; + __u16 dport; +}; + +/*value to masquerade_map*/ +struct masq_value { + union { + __u32 ip; + __u32 ip6[4]; + }__in46_u_origin; +}; + /*Key to tun_map*/ struct tun_key { union { @@ -282,6 +302,7 @@ struct diag_ip4 { bool ddos_filtering; bool ipv6_enable; bool outbound_filter; + bool masquerade; }; /*Value to tun_map*/ @@ -531,6 +552,14 @@ struct { __uint(pinning, LIBBPF_PIN_BY_NAME); } udp_map SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(key_size, sizeof(struct masq_key)); + __uint(value_size,sizeof(struct masq_value)); + __uint(max_entries, BPF_MAX_SESSIONS * 2); + __uint(pinning, LIBBPF_PIN_BY_NAME); +} masquerade_map SEC(".maps"); + /*tracks inbound allowed sessions*/ struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); @@ -613,6 +642,13 @@ static inline struct tcp_state *get_tcp(struct tuple_key key){ return ts; } +static inline struct masq_value *get_masquerade(struct masq_key key){ + struct masq_value *mv; + mv = bpf_map_lookup_elem(&masquerade_map, &key); + return mv; +} + + /*Insert entry into ingress tcp state table*/ static inline void insert_ingress_tcp(struct tcp_state tstate, struct tuple_key key){ bpf_map_update_elem(&tcp_ingress_map, &key, &tstate,0); @@ -1373,6 +1409,55 @@ int bpf_sk_splice(struct __sk_buff *skb){ } } } + if(local_diag->masquerade && local_ip4 && local_ip4->count && (local_ip4->ipaddr[0] == tuple->ipv4.daddr)){ + struct masq_key mk = {0}; + mk.__in46_u_dest.ip = tuple->ipv4.saddr; + mk.dport = tuple->ipv4.sport; + mk.sport = tuple->ipv4.dport; + mk.ifindex = skb->ifindex; + mk.protocol = IPPROTO_TCP; + struct masq_value *mv = get_masquerade(mk); + if(mv){ + __u32 l3_sum = bpf_csum_diff((__u32 *)&iph->daddr, sizeof(iph->daddr),(__u32 *)&mv->__in46_u_origin.ip, sizeof(mv->__in46_u_origin.ip), 0); + iph->daddr = mv->__in46_u_origin.ip; + /*Calculate l3 Checksum*/ + bpf_l3_csum_replace(skb, sizeof(struct ethhdr) + offsetof(struct iphdr, check), 0, l3_sum, 0); + iph = (struct iphdr *)(skb->data + sizeof(*eth)); + if ((unsigned long)(iph + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + tuple = (struct bpf_sock_tuple *)(void*)(long)&iph->saddr; + if(!tuple){ + return TC_ACT_SHOT; + } + tuple_len = sizeof(tuple->ipv4); + if ((unsigned long)tuple + tuple_len > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + tcph = (struct tcphdr *)((unsigned long)iph + sizeof(*iph)); + if ((unsigned long)(tcph + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + /*Calculate l4 Checksum*/ + bpf_l4_csum_replace(skb, sizeof(struct ethhdr) + sizeof(struct iphdr) + offsetof(struct tcphdr, check),local_ip4->ipaddr[0], iph->daddr, BPF_F_PSEUDO_HDR | 4); + iph = (struct iphdr *)(skb->data + sizeof(*eth)); + if ((unsigned long)(iph + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + tuple = (struct bpf_sock_tuple *)(void*)(long)&iph->saddr; + if(!tuple){ + return TC_ACT_SHOT; + } + tuple_len = sizeof(tuple->ipv4); + if ((unsigned long)tuple + tuple_len > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + tcph = (struct tcphdr *)((unsigned long)iph + sizeof(*iph)); + if ((unsigned long)(tcph + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + } + } tcp_state_key.__in46_u_dst.ip = tuple->ipv4.saddr; tcp_state_key.__in46_u_src.ip = tuple->ipv4.daddr; tcp_state_key.sport = tuple->ipv4.dport; @@ -1469,6 +1554,51 @@ int bpf_sk_splice(struct __sk_buff *skb){ bpf_sk_release(sk); /*reply to outbound passthrough check*/ }else{ + if(local_diag->masquerade && local_ip4 && local_ip4->count && (local_ip4->ipaddr[0] == tuple->ipv4.daddr)){ + struct masq_key mk = {0}; + mk.__in46_u_dest.ip = tuple->ipv4.saddr; + mk.dport = tuple->ipv4.sport; + mk.sport = tuple->ipv4.dport; + mk.ifindex = skb->ifindex; + mk.protocol = IPPROTO_UDP; + struct masq_value *mv = get_masquerade(mk); + if(mv){ + struct iphdr *iph = (struct iphdr *)(skb->data + sizeof(*eth)); + if ((unsigned long)(iph + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + __u32 l3_sum = bpf_csum_diff((__u32 *)&iph->daddr, sizeof(iph->daddr),(__u32 *)&mv->__in46_u_origin.ip, sizeof(mv->__in46_u_origin.ip), 0); + iph->daddr = mv->__in46_u_origin.ip; + /*Calculate l3 Checksum*/ + bpf_l3_csum_replace(skb, sizeof(struct ethhdr) + offsetof(struct iphdr, check), 0, l3_sum, 0); + iph = (struct iphdr *)(skb->data + sizeof(*eth)); + if ((unsigned long)(iph + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + tuple = (struct bpf_sock_tuple *)(void*)(long)&iph->saddr; + if(!tuple){ + return TC_ACT_SHOT; + } + tuple_len = sizeof(tuple->ipv4); + if ((unsigned long)tuple + tuple_len > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + /*Calculate l4 Checksum*/ + bpf_l4_csum_replace(skb, sizeof(struct ethhdr) + sizeof(struct iphdr) + offsetof(struct udphdr, check),local_ip4->ipaddr[0], iph->daddr, BPF_F_PSEUDO_HDR | 4); + iph = (struct iphdr *)(skb->data + sizeof(*eth)); + if ((unsigned long)(iph + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + tuple = (struct bpf_sock_tuple *)(void*)(long)&iph->saddr; + if(!tuple){ + return TC_ACT_SHOT; + } + tuple_len = sizeof(tuple->ipv4); + if ((unsigned long)tuple + tuple_len > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + } + } udp_state_key.__in46_u_dst.ip = tuple->ipv4.saddr; udp_state_key.__in46_u_src.ip = tuple->ipv4.daddr; udp_state_key.sport = tuple->ipv4.dport; @@ -1570,6 +1700,40 @@ int bpf_sk_splice(struct __sk_buff *skb){ bpf_sk_release(sk); /*reply to outbound passthrough check*/ }else{ + if(local_diag->masquerade && local_ip6 && local_ip6->count && (local_ip6->ipaddr[0][0] == tuple->ipv6.daddr[0]) + && (local_ip6->ipaddr[0][1] == tuple->ipv6.daddr[1]) && (local_ip6->ipaddr[0][2] == tuple->ipv6.daddr[2]) + && (local_ip6->ipaddr[0][3] == tuple->ipv6.daddr[3])){ + struct masq_key mk = {0}; + memcpy(mk.__in46_u_dest.ip6, tuple->ipv6.saddr, sizeof(tuple->ipv6.saddr)); + mk.dport = tuple->ipv6.sport; + mk.sport = tuple->ipv6.dport; + mk.ifindex = skb->ifindex; + mk.protocol = IPPROTO_TCP; + struct masq_value *mv = get_masquerade(mk); + if(mv){ + memcpy(ip6h->daddr.in6_u.u6_addr32, mv->__in46_u_origin.ip6, sizeof(mv->__in46_u_origin.ip6)); + /*Calculate l4 Checksum*/ + for(int x = 0; x < 4; x++){ + bpf_l4_csum_replace(skb, sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + offsetof(struct tcphdr, check), local_ip6->ipaddr[0][x], ip6h->daddr.in6_u.u6_addr32[x], BPF_F_PSEUDO_HDR | 4); + ip6h = (struct ipv6hdr *)(skb->data + sizeof(*eth)); + if ((unsigned long)(ip6h + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + tuple = (struct bpf_sock_tuple *)(void*)(long)&ip6h->saddr; + if(!tuple){ + return TC_ACT_SHOT; + } + tuple_len = sizeof(tuple->ipv6); + if ((unsigned long)tuple + tuple_len > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + tcph = (struct tcphdr *)((unsigned long)ip6h + sizeof(*ip6h)); + if ((unsigned long)(tcph + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + } + } + } memcpy(tcp_state_key.__in46_u_dst.ip6,tuple->ipv6.saddr, sizeof(tuple->ipv6.saddr)); memcpy(tcp_state_key.__in46_u_src.ip6,tuple->ipv6.daddr, sizeof(tuple->ipv6.daddr)); tcp_state_key.sport = tuple->ipv6.dport; @@ -1577,7 +1741,7 @@ int bpf_sk_splice(struct __sk_buff *skb){ unsigned long long tstamp = bpf_ktime_get_ns(); struct tcp_state *tstate = get_tcp(tcp_state_key); /*check tcp state and timeout if greater than 60 minutes without traffic*/ - if(tstate && (tstamp < (tstate->tstamp + 3600000000000))){ + if(tstate && (tstamp < (tstate->tstamp + 3600000000000))){ if(tcph->syn && tcph->ack){ tstate->ack =1; tstate->tstamp = tstamp; @@ -1661,6 +1825,40 @@ int bpf_sk_splice(struct __sk_buff *skb){ bpf_sk_release(sk); /*reply to outbound passthrough check*/ }else{ + if(local_diag->masquerade && local_ip6 && local_ip6->count && (local_ip6->ipaddr[0][0] == tuple->ipv6.daddr[0]) + && (local_ip6->ipaddr[0][1] == tuple->ipv6.daddr[1]) && (local_ip6->ipaddr[0][2] == tuple->ipv6.daddr[2]) + && (local_ip6->ipaddr[0][3] == tuple->ipv6.daddr[3])){ + struct ipv6hdr *ip6h = (struct ipv6hdr *)(skb->data + sizeof(*eth)); + if ((unsigned long)(ip6h + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + struct masq_key mk = {0}; + memcpy(mk.__in46_u_dest.ip6, tuple->ipv6.saddr, sizeof(tuple->ipv6.saddr)); + mk.dport = tuple->ipv6.sport; + mk.sport = tuple->ipv6.dport; + mk.ifindex = skb->ifindex; + mk.protocol = IPPROTO_UDP; + struct masq_value *mv = get_masquerade(mk); + if(mv){ + memcpy(ip6h->daddr.in6_u.u6_addr32, mv->__in46_u_origin.ip6, sizeof(ip6h->daddr)); + /*Calculate l4 Checksum*/ + for(int x = 0; x < 4; x++){ + bpf_l4_csum_replace(skb, sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + offsetof(struct udphdr, check), local_ip6->ipaddr[0][x], ip6h->daddr.in6_u.u6_addr32[x], BPF_F_PSEUDO_HDR | 4); + ip6h = (struct ipv6hdr *)(skb->data + sizeof(*eth)); + if ((unsigned long)(ip6h + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + tuple = (struct bpf_sock_tuple *)(void*)(long)&ip6h->saddr; + if(!tuple){ + return TC_ACT_SHOT; + } + tuple_len = sizeof(tuple->ipv6); + if ((unsigned long)tuple + tuple_len > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + } + } + } memcpy(udp_state_key.__in46_u_dst.ip6,tuple->ipv6.saddr, sizeof(tuple->ipv6.saddr)); memcpy(udp_state_key.__in46_u_src.ip6,tuple->ipv6.daddr, sizeof(tuple->ipv6.daddr)); udp_state_key.sport = tuple->ipv6.dport; diff --git a/src/zfw_tc_outbound_track.c b/src/zfw_tc_outbound_track.c index 5c3e953..52d2b83 100644 --- a/src/zfw_tc_outbound_track.c +++ b/src/zfw_tc_outbound_track.c @@ -64,7 +64,6 @@ #define IPV6_TUPLE_TOO_BIG 31 #define memcpy(dest, src, n) __builtin_memcpy((dest), (src), (n)) - struct bpf_event{ __u8 version; unsigned long long tstamp; @@ -117,6 +116,26 @@ struct udp_state { unsigned long long tstamp; }; +/*Key to masquerade_map*/ +struct masq_key { + uint32_t ifindex; + union { + __u32 ip; + __u32 ip6[4]; + }__in46_u_dest; + __u8 protocol; + __u16 sport; + __u16 dport; +}; + +/*value to masquerade_map*/ +struct masq_value { + union { + __u32 ip; + __u32 ip6[4]; + }__in46_u_origin; +}; + /*value to diag_map*/ struct diag_ip4 { bool echo; @@ -131,6 +150,7 @@ struct diag_ip4 { bool ddos_filtering; bool ipv6_enable; bool outbound_filter; + bool masquerade; }; /*value to ifindex_tun_map*/ @@ -257,6 +277,14 @@ struct { __uint(pinning, LIBBPF_PIN_BY_NAME); } egress_matched_map SEC(".maps"); +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __uint(key_size, sizeof(struct masq_key)); + __uint(value_size,sizeof(struct masq_value)); + __uint(max_entries, BPF_MAX_SESSIONS * 2); + __uint(pinning, LIBBPF_PIN_BY_NAME); +} masquerade_map SEC(".maps"); + struct { __uint(type, BPF_MAP_TYPE_LRU_HASH); __uint(key_size, sizeof(struct match6_key)); @@ -436,6 +464,11 @@ static inline void insert_tcp(struct tcp_state tstate, struct tuple_key key){ bpf_map_update_elem(&tcp_map, &key, &tstate,0); } +static inline void insert_masquerade(struct masq_value mv, struct masq_key key){ + bpf_map_update_elem(&masquerade_map, &key, &mv,0); +} + + /*Remove entry into tcp state table*/ static inline void del_tcp(struct tuple_key key){ bpf_map_delete_elem(&tcp_map, &key); @@ -920,8 +953,6 @@ int bpf_sk_splice(struct __sk_buff *skb){ if(tcp) { - - event.proto = IPPROTO_TCP; struct tcphdr *tcph = (struct tcphdr *)((unsigned long)iph + sizeof(*iph)); if ((unsigned long)(tcph + 1) > (unsigned long)skb->data_end){ @@ -1969,6 +2000,12 @@ int bpf_sk_splice6(struct __sk_buff *skb){ return TC_ACT_OK; } + //get ipv4 interface addr mappings + struct ifindex_ip4 *local_ip4 = get_local_ip4(skb->ifindex); + + //get ipv6 interface addr mappings + struct ifindex_ip6 *local_ip6 = get_local_ip6(skb->ifindex); + /*get entry from tun ifindex map*/ struct ifindex_tun *tun_if = get_tun_index(0); @@ -2060,6 +2097,55 @@ int bpf_sk_splice6(struct __sk_buff *skb){ tcp_state_key.__in46_u_dst.ip = tuple->ipv4.daddr; tcp_state_key.sport = tuple->ipv4.sport; tcp_state_key.dport = tuple->ipv4.dport; + if(local_diag->masquerade && local_ip4 && local_ip4->count){ + __u32 l3_sum = bpf_csum_diff((__u32 *)&tuple->ipv4.saddr, sizeof(tuple->ipv4.saddr), (__u32 *)&local_ip4->ipaddr[0], sizeof(local_ip4->ipaddr[0]), 0); + struct masq_value mv = {0}; + mv.__in46_u_origin.ip = tuple->ipv4.saddr; + struct masq_key mk = {0}; + mk.__in46_u_dest.ip = tuple->ipv4.daddr; + mk.dport = tuple->ipv4.dport; + mk.sport = tuple->ipv4.sport; + mk.ifindex = skb->ifindex; + mk.protocol = IPPROTO_TCP; + insert_masquerade(mv, mk); + iph->saddr = local_ip4->ipaddr[0]; + /*Calculate l3 Checksum*/ + bpf_l3_csum_replace(skb, sizeof(struct ethhdr) + offsetof(struct iphdr, check), 0, l3_sum, 0); + iph = (struct iphdr *)(skb->data + sizeof(*eth)); + if ((unsigned long)(iph + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + tuple = (struct bpf_sock_tuple *)(void*)(long)&iph->saddr; + if(!tuple){ + return TC_ACT_SHOT; + } + tuple_len = sizeof(tuple->ipv4); + if ((unsigned long)tuple + tuple_len > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + tcph = (struct tcphdr *)((unsigned long)iph + sizeof(*iph)); + if ((unsigned long)(tcph + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + /*Calculate l4 Checksum*/ + bpf_l4_csum_replace(skb, sizeof(struct ethhdr) + sizeof(struct iphdr) + offsetof(struct tcphdr, check), mv.__in46_u_origin.ip, iph->saddr, BPF_F_PSEUDO_HDR | 4); + iph = (struct iphdr *)(skb->data + sizeof(*eth)); + if ((unsigned long)(iph + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + tuple = (struct bpf_sock_tuple *)(void*)(long)&iph->saddr; + if(!tuple){ + return TC_ACT_SHOT; + } + tuple_len = sizeof(tuple->ipv4); + if ((unsigned long)tuple + tuple_len > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + tcph = (struct tcphdr *)((unsigned long)iph + sizeof(*iph)); + if ((unsigned long)(tcph + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + } unsigned long long tstamp = bpf_ktime_get_ns(); struct tcp_state *tstate; if(tcph->syn && !tcph->ack){ @@ -2164,6 +2250,47 @@ int bpf_sk_splice6(struct __sk_buff *skb){ udp_state_key.__in46_u_dst.ip = tuple->ipv4.daddr; udp_state_key.sport = tuple->ipv4.sport; udp_state_key.dport = tuple->ipv4.dport; + if(local_diag->masquerade && local_ip4 && local_ip4->count){ + __u32 l3_sum = bpf_csum_diff((__u32 *)&tuple->ipv4.saddr, sizeof(tuple->ipv4.saddr), (__u32 *)&local_ip4->ipaddr[0], sizeof(local_ip4->ipaddr[0]), 0); + struct masq_value mv = {0}; + mv.__in46_u_origin.ip = tuple->ipv4.saddr; + struct masq_key mk = {0}; + mk.__in46_u_dest.ip = tuple->ipv4.daddr; + mk.dport = tuple->ipv4.dport; + mk.sport = tuple->ipv4.sport; + mk.ifindex = skb->ifindex; + mk.protocol = IPPROTO_UDP; + insert_masquerade(mv, mk); + iph->saddr = local_ip4->ipaddr[0]; + /*Calculate l3 Checksum*/ + bpf_l3_csum_replace(skb, sizeof(struct ethhdr) + offsetof(struct iphdr, check), 0, l3_sum, 0); + iph = (struct iphdr *)(skb->data + sizeof(*eth)); + if ((unsigned long)(iph + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + tuple = (struct bpf_sock_tuple *)(void*)(long)&iph->saddr; + if(!tuple){ + return TC_ACT_SHOT; + } + tuple_len = sizeof(tuple->ipv4); + if ((unsigned long)tuple + tuple_len > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + /*Calculate l4 Checksum*/ + bpf_l4_csum_replace(skb, sizeof(struct ethhdr) + sizeof(struct iphdr) + offsetof(struct udphdr, check), mv.__in46_u_origin.ip, iph->saddr, BPF_F_PSEUDO_HDR | 4); + iph = (struct iphdr *)(skb->data + sizeof(*eth)); + if ((unsigned long)(iph + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + tuple = (struct bpf_sock_tuple *)(void*)(long)&iph->saddr; + if(!tuple){ + return TC_ACT_SHOT; + } + tuple_len = sizeof(tuple->ipv4); + if ((unsigned long)tuple + tuple_len > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + } struct udp_state *ustate = get_udp(udp_state_key); if((!ustate) || (ustate->tstamp > (tstamp + 30000000000))){ struct udp_state us = { @@ -2232,6 +2359,38 @@ int bpf_sk_splice6(struct __sk_buff *skb){ memcpy(tcp_state_key.__in46_u_dst.ip6,tuple->ipv6.daddr, sizeof(tuple->ipv6.daddr)); tcp_state_key.sport = tuple->ipv6.sport; tcp_state_key.dport = tuple->ipv6.dport; + if(local_diag->masquerade && local_ip6 && local_ip6->count){ + struct masq_value mv = {0}; + memcpy(mv.__in46_u_origin.ip6, tuple->ipv6.saddr, sizeof(mv.__in46_u_origin.ip6)); + struct masq_key mk = {0}; + memcpy(mk.__in46_u_dest.ip6, tuple->ipv6.daddr, sizeof(tuple->ipv6.daddr)); + mk.dport = tuple->ipv6.dport; + mk.sport = tuple->ipv6.sport; + mk.ifindex = skb->ifindex; + mk.protocol = IPPROTO_TCP; + insert_masquerade(mv, mk); + memcpy(ip6h->saddr.in6_u.u6_addr32, local_ip6->ipaddr[0], sizeof(local_ip6->ipaddr[0])); + /*Calculate l4 Checksum*/ + for(int x = 0; x < 4; x++){ + bpf_l4_csum_replace(skb, sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + offsetof(struct tcphdr, check), mv.__in46_u_origin.ip6[x], ip6h->saddr.in6_u.u6_addr32[x], BPF_F_PSEUDO_HDR | 4); + ip6h = (struct ipv6hdr *)(skb->data + sizeof(*eth)); + if ((unsigned long)(ip6h + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + tuple = (struct bpf_sock_tuple *)(void*)(long)&ip6h->saddr; + if(!tuple){ + return TC_ACT_SHOT; + } + tuple_len = sizeof(tuple->ipv6); + if ((unsigned long)tuple + tuple_len > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + tcph = (struct tcphdr *)((unsigned long)ip6h + sizeof(*ip6h)); + if ((unsigned long)(tcph + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + } + } unsigned long long tstamp = bpf_ktime_get_ns(); struct tcp_state *tstate; if(tcph->syn && !tcph->ack){ @@ -2333,6 +2492,38 @@ int bpf_sk_splice6(struct __sk_buff *skb){ memcpy(udp_state_key.__in46_u_dst.ip6,tuple->ipv6.daddr, sizeof(tuple->ipv6.daddr)); udp_state_key.sport = tuple->ipv6.sport; udp_state_key.dport = tuple->ipv6.dport; + if(local_diag->masquerade && local_ip6 && local_ip6->count){ + struct masq_value mv = {0}; + memcpy(mv.__in46_u_origin.ip6, tuple->ipv6.saddr, sizeof(mv.__in46_u_origin.ip6)); + struct masq_key mk = {0}; + memcpy(mk.__in46_u_dest.ip6, tuple->ipv6.daddr, sizeof(tuple->ipv6.daddr)); + mk.dport = tuple->ipv6.dport; + mk.sport = tuple->ipv6.sport; + mk.ifindex = skb->ifindex; + mk.protocol = IPPROTO_UDP; + insert_masquerade(mv, mk ); + memcpy(ip6h->saddr.in6_u.u6_addr32, local_ip6->ipaddr[0], sizeof(local_ip6->ipaddr[0])); + /*Calculate l4 Checksum*/ + for(int x = 0; x < 4; x++){ + bpf_l4_csum_replace(skb, sizeof(struct ethhdr) + sizeof(struct ipv6hdr) + offsetof(struct udphdr, check), mv.__in46_u_origin.ip6[x], ip6h->saddr.in6_u.u6_addr32[x], BPF_F_PSEUDO_HDR | 4); + ip6h = (struct ipv6hdr *)(skb->data + sizeof(*eth)); + if ((unsigned long)(ip6h + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + tuple = (struct bpf_sock_tuple *)(void*)(long)&ip6h->saddr; + if(!tuple){ + return TC_ACT_SHOT; + } + tuple_len = sizeof(tuple->ipv6); + if ((unsigned long)tuple + tuple_len > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + udph = (struct udphdr *)((unsigned long)ip6h + sizeof(*ip6h)); + if ((unsigned long)(udph + 1) > (unsigned long)skb->data_end){ + return TC_ACT_SHOT; + } + } + } struct udp_state *ustate = get_udp(udp_state_key); if((!ustate) || (ustate->tstamp > (tstamp + 30000000000))){ struct udp_state us = {