| // SPDX-License-Identifier: GPL-2.0 |
| #include "vmlinux.h" |
| #include <bpf/bpf_helpers.h> |
| #include <bpf/bpf_endian.h> |
| |
| /* 127.0.0.1 in host byte order */ |
| #define SERVER_IP 0x7F000001 |
| /* ::1 in host byte order (last 32-bit word) */ |
| #define SERVER_IP6_LO 0x00000001 |
| |
| __u16 server_port = 0; |
| int unreach_type = 0; |
| int unreach_code = 0; |
| int kfunc_ret = -1; |
| int target_pid = -1; |
| |
| unsigned int rec_count = 0; |
| int rec_kfunc_rets[] = { -1, -1 }; |
| |
| SEC("cgroup_skb/egress") |
| int egress(struct __sk_buff *skb) |
| { |
| void *data = (void *)(long)skb->data; |
| void *data_end = (void *)(long)skb->data_end; |
| struct iphdr *iph; |
| struct ipv6hdr *ip6h; |
| struct tcphdr *tcph; |
| __u8 version; |
| |
| if (data + 1 > data_end) |
| return SK_PASS; |
| |
| version = (*((__u8 *)data)) >> 4; |
| |
| if (version == 4) { |
| iph = data; |
| if ((void *)(iph + 1) > data_end || |
| iph->protocol != IPPROTO_TCP || |
| iph->daddr != bpf_htonl(SERVER_IP)) |
| return SK_PASS; |
| |
| tcph = (void *)iph + iph->ihl * 4; |
| if ((void *)(tcph + 1) > data_end || |
| tcph->dest != bpf_htons(server_port)) |
| return SK_PASS; |
| |
| } else if (version == 6) { |
| ip6h = data; |
| if ((void *)(ip6h + 1) > data_end || |
| ip6h->nexthdr != IPPROTO_TCP) |
| return SK_PASS; |
| |
| if (ip6h->daddr.in6_u.u6_addr32[0] != 0 || |
| ip6h->daddr.in6_u.u6_addr32[1] != 0 || |
| ip6h->daddr.in6_u.u6_addr32[2] != 0 || |
| ip6h->daddr.in6_u.u6_addr32[3] != bpf_htonl(SERVER_IP6_LO)) |
| return SK_PASS; |
| |
| tcph = (void *)(ip6h + 1); |
| if ((void *)(tcph + 1) > data_end || |
| tcph->dest != bpf_htons(server_port)) |
| return SK_PASS; |
| } else { |
| return SK_PASS; |
| } |
| |
| kfunc_ret = bpf_icmp_send(skb, unreach_type, unreach_code); |
| |
| return SK_DROP; |
| } |
| |
| SEC("cgroup_skb/egress") |
| int recursion(struct __sk_buff *skb) |
| { |
| void *data = (void *)(long)skb->data; |
| void *data_end = (void *)(long)skb->data_end; |
| struct icmphdr *icmph; |
| struct tcphdr *tcph; |
| struct iphdr *iph; |
| int ret; |
| |
| if ((bpf_get_current_pid_tgid() >> 32) != target_pid) |
| return SK_PASS; |
| |
| iph = data; |
| if ((void *)(iph + 1) > data_end || iph->version != 4) |
| return SK_PASS; |
| |
| if (iph->daddr != bpf_htonl(SERVER_IP)) |
| return SK_PASS; |
| |
| if (iph->protocol == IPPROTO_TCP) { |
| tcph = (void *)iph + iph->ihl * 4; |
| if ((void *)(tcph + 1) > data_end || |
| tcph->dest != bpf_htons(server_port)) |
| return SK_PASS; |
| } else if (iph->protocol == IPPROTO_ICMP) { |
| icmph = (void *)iph + iph->ihl * 4; |
| if ((void *)(icmph + 1) > data_end || |
| icmph->type != unreach_type || icmph->code != unreach_code) |
| return SK_PASS; |
| } else { |
| return SK_PASS; |
| } |
| |
| /* |
| * This call will provoke a recursion: the ICMP packet generated by the |
| * kfunc will re-trigger this program since we are in the root cgroup in |
| * which the kernel ICMP socket belongs. However when re-entering the |
| * kfunc, it should return EBUSY. |
| */ |
| ret = bpf_icmp_send(skb, unreach_type, unreach_code); |
| rec_kfunc_rets[rec_count & 1] = ret; |
| __sync_fetch_and_add(&rec_count, 1); |
| |
| /* Let the first ICMP error message pass */ |
| if (iph->protocol == IPPROTO_ICMP) |
| return SK_PASS; |
| |
| return SK_DROP; |
| } |
| |
| char LICENSE[] SEC("license") = "Dual BSD/GPL"; |