diff --git a/README.md b/README.md index e3b30ba..0cdd581 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,6 @@ sudo tee /etc/sysctl.d/60-dae-$lan_ifname.conf << EOF net.ipv4.conf.$lan_ifname.forwarding = 1 net.ipv6.conf.$lan_ifname.forwarding = 1 net.ipv4.conf.$lan_ifname.send_redirects = 0 -net.ipv6.conf.$lan_ifname.send_redirects = 0 EOF sudo sysctl --system ``` diff --git a/control/control_plane.go b/control/control_plane.go index edf75c5..ba2ad76 100644 --- a/control/control_plane.go +++ b/control/control_plane.go @@ -153,22 +153,6 @@ func NewControlPlane( if err = bpf.ParamMap.Update(consts.ControlPlanePidKey, uint32(os.Getpid()), ebpf.UpdateAny); err != nil { return nil, err } - // Write ip_proto to hdr_size mapping for IPv6 extension extraction (it is just for eBPF code insns optimization). - if err = bpf.IpprotoHdrsizeMap.Update(uint32(unix.IPPROTO_HOPOPTS), int32(-1), ebpf.UpdateAny); err != nil { - return nil, err - } - if err = bpf.IpprotoHdrsizeMap.Update(uint32(unix.IPPROTO_ROUTING), int32(-1), ebpf.UpdateAny); err != nil { - return nil, err - } - if err = bpf.IpprotoHdrsizeMap.Update(uint32(unix.IPPROTO_FRAGMENT), int32(4), ebpf.UpdateAny); err != nil { - return nil, err - } - if err = bpf.IpprotoHdrsizeMap.Update(uint32(unix.IPPROTO_TCP), int32(-2), ebpf.UpdateAny); err != nil { - return nil, err - } - if err = bpf.IpprotoHdrsizeMap.Update(uint32(unix.IPPROTO_UDP), int32(-2), ebpf.UpdateAny); err != nil { - return nil, err - } /// Bind to links. Binding should be advance of dialerGroups to avoid un-routable old connection. // Add clsact qdisc diff --git a/control/kern/headers b/control/kern/headers index 99eb2eb..378c3c5 160000 --- a/control/kern/headers +++ b/control/kern/headers @@ -1 +1 @@ -Subproject commit 99eb2ebd74b10f2b91c7840c83ad08eec8f50e59 +Subproject commit 378c3c576e0f4c785a3d5e71400b552725527f30 diff --git a/control/kern/tproxy.c b/control/kern/tproxy.c index 182ae67..c0974b0 100644 --- a/control/kern/tproxy.c +++ b/control/kern/tproxy.c @@ -9,6 +9,7 @@ #include "headers/if_ether_defs.h" #include "headers/pkt_cls_defs.h" #include "headers/socket_defs.h" +#include "headers/upai_in6_defs.h" #include "headers/vmlinux.h" #include "headers/bpf_core_read.h" @@ -217,15 +218,6 @@ struct { __uint(max_entries, 2); } l4proto_ipversion_map SEC(".maps"); -// IPPROTO to hdr_size -struct { - __uint(type, BPF_MAP_TYPE_HASH); - __type(key, __u32); - __type(value, __s32); - __uint(max_entries, 5); - __uint(pinning, LIBBPF_PIN_BY_NAME); -} ipproto_hdrsize_map SEC(".maps"); - // Interface Ips: struct if_params { bool rx_cksm_offload; @@ -543,13 +535,11 @@ static __always_inline int rewrite_port(struct __sk_buff *skb, __u8 proto, return 0; } -static __always_inline int handle_ipv6_extensions(const struct __sk_buff *skb, - __u32 offset, __u32 hdr, - struct tcphdr *tcph, - struct udphdr *udph, - __u8 *ihl, __u8 *l4proto) { +static __always_inline int +handle_ipv6_extensions(const struct __sk_buff *skb, __u32 offset, __u32 hdr, + struct icmp6hdr *icmp6h, struct tcphdr *tcph, + struct udphdr *udph, __u8 *ihl, __u8 *l4proto) { __u8 hdr_length = 0; - __s32 *p_s32; __u8 nexthdr = 0; *ihl = sizeof(struct ipv6hdr) / 4; int ret; @@ -563,12 +553,20 @@ static __always_inline int handle_ipv6_extensions(const struct __sk_buff *skb, return 1; } // See control/control_plane.go. - if (!(p_s32 = bpf_map_lookup_elem(&ipproto_hdrsize_map, &hdr))) { - return 1; - } - switch (*p_s32) { - case -1: + switch (hdr) { + case IPPROTO_ICMPV6: + *l4proto = hdr; + hdr_length = sizeof(struct icmp6hdr); + // Assume ICMPV6 as a level 4 protocol. + if ((ret = bpf_skb_load_bytes(skb, offset, icmp6h, hdr_length))) { + bpf_printk("not a valid IPv6 packet"); + return -EFAULT; + } + return 0; + + case IPPROTO_HOPOPTS: + case IPPROTO_ROUTING: if ((ret = bpf_skb_load_bytes(skb, offset + 1, &hdr_length, sizeof(hdr_length)))) { bpf_printk("not a valid IPv6 packet"); @@ -580,13 +578,13 @@ static __always_inline int handle_ipv6_extensions(const struct __sk_buff *skb, return -EFAULT; } break; - case 4: + case IPPROTO_FRAGMENT: hdr_length = 4; goto special_n1; - case -2: + case IPPROTO_TCP: + case IPPROTO_UDP: *l4proto = hdr; if (hdr == IPPROTO_TCP) { - __builtin_memset(tcph, 0, sizeof(struct udphdr)); // Upper layer; if ((ret = bpf_skb_load_bytes(skb, offset, tcph, sizeof(struct tcphdr)))) { @@ -594,7 +592,6 @@ static __always_inline int handle_ipv6_extensions(const struct __sk_buff *skb, return -EFAULT; } } else if (hdr == IPPROTO_UDP) { - __builtin_memset(tcph, 0, sizeof(struct tcphdr)); // Upper layer; if ((ret = bpf_skb_load_bytes(skb, offset, udph, sizeof(struct udphdr)))) { @@ -616,11 +613,10 @@ static __always_inline int handle_ipv6_extensions(const struct __sk_buff *skb, return 1; } -static __always_inline int -parse_transport(const struct __sk_buff *skb, struct ethhdr *ethh, - struct iphdr *iph, struct ipv6hdr *ipv6h, struct tcphdr *tcph, - struct udphdr *udph, __u8 *ihl, __u8 *ipversion, - __u8 *l4proto) { +static __always_inline int parse_transport( + const struct __sk_buff *skb, struct ethhdr *ethh, struct iphdr *iph, + struct ipv6hdr *ipv6h, struct icmp6hdr *icmp6h, struct tcphdr *tcph, + struct udphdr *udph, __u8 *ihl, __u8 *ipversion, __u8 *l4proto) { __u32 offset = 0; int ret; @@ -635,11 +631,15 @@ parse_transport(const struct __sk_buff *skb, struct ethhdr *ethh, *ipversion = 0; *ihl = 0; *l4proto = 0; + __builtin_memset(iph, 0, sizeof(struct iphdr)); + __builtin_memset(ipv6h, 0, sizeof(struct ipv6hdr)); + __builtin_memset(icmp6h, 0, sizeof(struct icmp6hdr)); + __builtin_memset(tcph, 0, sizeof(struct tcphdr)); + __builtin_memset(udph, 0, sizeof(struct udphdr)); // bpf_printk("parse_transport: h_proto: %u ? %u %u", eth->h_proto, // bpf_htons(ETH_P_IP), bpf_htons(ETH_P_IPV6)); if (ethh->h_proto == bpf_htons(ETH_P_IP)) { - __builtin_memset(ipv6h, 0, sizeof(struct ipv6hdr)); *ipversion = 4; if ((ret = bpf_skb_load_bytes(skb, offset, iph, sizeof(struct iphdr)))) { @@ -651,14 +651,12 @@ parse_transport(const struct __sk_buff *skb, struct ethhdr *ethh, // We only process TCP and UDP traffic. *l4proto = iph->protocol; if (iph->protocol == IPPROTO_TCP) { - __builtin_memset(udph, 0, sizeof(struct udphdr)); if ((ret = bpf_skb_load_bytes(skb, offset, tcph, sizeof(struct tcphdr)))) { // Not a complete tcphdr. return -EFAULT; } } else if (iph->protocol == IPPROTO_UDP) { - __builtin_memset(tcph, 0, sizeof(struct tcphdr)); if ((ret = bpf_skb_load_bytes(skb, offset, udph, sizeof(struct udphdr)))) { // Not a complete tcphdr. @@ -671,7 +669,6 @@ parse_transport(const struct __sk_buff *skb, struct ethhdr *ethh, *ihl = iph->ihl; return 0; } else if (ethh->h_proto == bpf_htons(ETH_P_IPV6)) { - __builtin_memset(iph, 0, sizeof(struct iphdr)); *ipversion = 6; if ((ret = @@ -682,8 +679,8 @@ parse_transport(const struct __sk_buff *skb, struct ethhdr *ethh, offset += sizeof(struct ipv6hdr); - return handle_ipv6_extensions(skb, offset, ipv6h->nexthdr, tcph, udph, ihl, - l4proto); + return handle_ipv6_extensions(skb, offset, ipv6h->nexthdr, icmp6h, tcph, + udph, ihl, l4proto); } else { return 1; } @@ -1164,18 +1161,28 @@ int tproxy_lan_egress(struct __sk_buff *skb) { struct ethhdr ethh; struct iphdr iph; struct ipv6hdr ipv6h; + struct icmp6hdr icmp6h; struct tcphdr tcph; struct udphdr udph; __u8 ihl; __u8 ipversion; __u8 l4proto; - int ret = parse_transport(skb, ðh, &iph, &ipv6h, &tcph, &udph, &ihl, - &ipversion, &l4proto); + int ret = parse_transport(skb, ðh, &iph, &ipv6h, &icmp6h, &tcph, &udph, + &ihl, &ipversion, &l4proto); if (ret) { bpf_printk("parse_transport: %d", ret); return TC_ACT_OK; } - if (l4proto != IPPROTO_UDP) { + switch (l4proto) { + case IPPROTO_ICMPV6: + if (icmp6h.icmp6_type == 137) { + // REDIRECT (NDP) + return TC_ACT_SHOT; + } + return TC_ACT_PIPE; + case IPPROTO_UDP: + break; + default: return TC_ACT_PIPE; } @@ -1218,17 +1225,21 @@ int tproxy_lan_ingress(struct __sk_buff *skb) { struct ethhdr ethh; struct iphdr iph; struct ipv6hdr ipv6h; + struct icmp6hdr icmp6h; struct tcphdr tcph; struct udphdr udph; __u8 ihl; __u8 ipversion; __u8 l4proto; - int ret = parse_transport(skb, ðh, &iph, &ipv6h, &tcph, &udph, &ihl, - &ipversion, &l4proto); + int ret = parse_transport(skb, ðh, &iph, &ipv6h, &icmp6h, &tcph, &udph, + &ihl, &ipversion, &l4proto); if (ret) { bpf_printk("parse_transport: %d", ret); return TC_ACT_OK; } + if (l4proto == IPPROTO_ICMPV6) { + return TC_ACT_OK; + } // Prepare five tuples. struct tuples tuples; @@ -1492,17 +1503,21 @@ int tproxy_wan_egress(struct __sk_buff *skb) { struct ethhdr ethh; struct iphdr iph; struct ipv6hdr ipv6h; + struct icmp6hdr icmp6h; struct tcphdr tcph; struct udphdr udph; __u8 ihl; __u8 ipversion; __u8 l4proto; bool tcp_state_syn; - int ret = parse_transport(skb, ðh, &iph, &ipv6h, &tcph, &udph, &ihl, - &ipversion, &l4proto); + int ret = parse_transport(skb, ðh, &iph, &ipv6h, &icmp6h, &tcph, &udph, + &ihl, &ipversion, &l4proto); if (ret) { return TC_ACT_OK; } + if (l4proto == IPPROTO_ICMPV6) { + return TC_ACT_OK; + } // Backup for further use. struct tuples tuples; @@ -1546,7 +1561,7 @@ int tproxy_wan_egress(struct __sk_buff *skb) { if (sk) { // Not a tproxy WAN response. It is a tproxy LAN response. bpf_sk_release(sk); - return TC_ACT_OK; + return TC_ACT_PIPE; } } } @@ -1812,16 +1827,20 @@ int tproxy_wan_ingress(struct __sk_buff *skb) { struct ethhdr ethh; struct iphdr iph; struct ipv6hdr ipv6h; + struct icmp6hdr icmp6h; struct tcphdr tcph; struct udphdr udph; __u8 ihl; __u8 ipversion; __u8 l4proto; - int ret = parse_transport(skb, ðh, &iph, &ipv6h, &tcph, &udph, &ihl, - &ipversion, &l4proto); + int ret = parse_transport(skb, ðh, &iph, &ipv6h, &icmp6h, &tcph, &udph, + &ihl, &ipversion, &l4proto); if (ret) { return TC_ACT_OK; } + if (l4proto == IPPROTO_ICMPV6) { + return TC_ACT_OK; + } struct tuples tuples; get_tuples(&tuples, &iph, &ipv6h, &tcph, &udph, ipversion, l4proto); diff --git a/control/utils.go b/control/utils.go index 5cd92da..02bd602 100644 --- a/control/utils.go +++ b/control/utils.go @@ -87,10 +87,6 @@ func checkIpforward(ifname string, ipversion consts.IpVersionStr) error { path := fmt.Sprintf("/proc/sys/net/ipv%v/conf/%v/forwarding", ipversion, ifname) b, err := os.ReadFile(path) if err != nil { - if os.IsNotExist(err) { - // Kernel does not support. - return nil - } return err } if bytes.Equal(bytes.TrimSpace(b), []byte("1")) { @@ -113,10 +109,6 @@ func checkSendRedirects(ifname string, ipversion consts.IpVersionStr) error { path := fmt.Sprintf("/proc/sys/net/ipv%v/conf/%v/send_redirects", ipversion, ifname) b, err := os.ReadFile(path) if err != nil { - if os.IsNotExist(err) { - // Kernel does not support. - return nil - } return err } if bytes.Equal(bytes.TrimSpace(b), []byte("0")) { @@ -129,9 +121,6 @@ func CheckSendRedirects(ifname string) error { if err := checkSendRedirects(ifname, consts.IpVersionStr_4); err != nil { return err } - if err := checkSendRedirects(ifname, consts.IpVersionStr_6); err != nil { - return err - } return nil }