GPL/HostIsolation/TcFilter/TcFilter.bpf.c (151 lines of code) (raw):

// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause /* * Copyright (C) 2021 Elasticsearch BV * * This software is dual-licensed under the BSD 2-Clause and GPL v2 licenses. * You may choose either one of them if you use this software. */ #include "Kerneldefs.h" #include "TcFilterdefs.h" #include "bpf_endian.h" #include "bpf_helpers.h" #ifndef __section #define __section(NAME) __attribute__((section(NAME), used)) #endif // you took NULL for granted didn't you :) #define NULL 0 #define BPF_F_NO_PREALLOC (1U << 0) /* Key of an a BPF_MAP_TYPE_LPM_TRIE entry */ struct bpf_lpm_trie_key { __u32 prefixlen; /* up to 32 for AF_INET, 128 for AF_INET6 */ __u32 data; /* Arbitrary size */ }; struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(key_size, sizeof(int)); __uint(value_size, sizeof(int)); __uint(max_entries, 512); __uint(pinning, LIBBPF_PIN_BY_NAME); } allowed_IPs __section(".maps"); struct { __uint(type, BPF_MAP_TYPE_LPM_TRIE); __uint(key_size, 8); __uint(value_size, sizeof(int)); __uint(max_entries, 256); __uint(pinning, LIBBPF_PIN_BY_NAME); __uint(map_flags, BPF_F_NO_PREALLOC); } allowed_subnets __section(".maps"); __attribute__((always_inline)) static int allow_destination_IP(struct iphdr *ip) { __u32 *elem = NULL; struct bpf_lpm_trie_key lpm_key = { .prefixlen = 32, .data = ip->daddr, }; /* Check allowed IPs map first */ elem = bpf_map_lookup_elem(&allowed_IPs, &ip->daddr); if (elem) { /* IP is allowed */ return 1; } /* Now check allowed subnets */ elem = bpf_map_lookup_elem(&allowed_subnets, &lpm_key); if (!elem) { /* destination IP not within any allowed subnets - reject */ return 0; } else { /* IP matches allowed subnet */ return 1; } } __attribute__((always_inline)) static int allow_tcp_pkt_egress(struct tcphdr *tcp, struct iphdr *ip) { /* TCP packets are currently only filtered based on destination IP */ return allow_destination_IP(ip); } __attribute__((always_inline)) static int allow_udp_pkt_egress(struct udphdr *udp) { if ((DNS_PORT == bpf_ntohs(udp->source)) || (DNS_PORT == bpf_ntohs(udp->dest))) { /* allow DNS port (both client and server) */ return 1; #if 0 //TODO: check QDCOUNT==1 for sanity (it's always 1 for any DNS query) if ((__u8 *)(udp + 1) + 6 > skb->data_end) { return 0; } __u16 *dns = udp + 1; #endif } else if (((DHCP_SERVER_PORT == bpf_ntohs(udp->dest)) && (DHCP_CLIENT_PORT == bpf_ntohs(udp->source))) || ((DHCP_CLIENT_PORT == bpf_ntohs(udp->dest)) && (DHCP_SERVER_PORT == bpf_ntohs(udp->source)))) { /* allow DHCP ports (both client and server) */ return 1; } else { /* drop packet */ return 0; } } int classifier(struct __sk_buff *skb) { struct ethhdr *eth = NULL; struct iphdr *ip = NULL; void *data_end = (void *)(long)skb->data_end; void *data = (void *)(long)skb->data; __u32 eth_proto = 0; int rv = DROP_PACKET; if (data + sizeof(struct ethhdr) > data_end) { /* packet too small */ rv = DROP_PACKET; goto out; } eth = data; eth_proto = eth->h_proto; /* check L3 protocol */ if (eth_proto == bpf_htons(ETH_P_ARP)) { /* allow ARP */ rv = ALLOW_PACKET; goto out; } if (eth_proto != bpf_htons(ETH_P_IP)) { /* drop protocols other than IPv4 and ARP */ rv = DROP_PACKET; goto out; } ip = data + sizeof(struct ethhdr); if (ip + 1 > data_end) { rv = DROP_PACKET; goto out; } if (4 != ip->version) { /* drop IPv6 */ rv = DROP_PACKET; goto out; } if (5 != ip->ihl) { /* drop packets with IP options (5 == 20 bytes == min IP header size ) */ rv = DROP_PACKET; goto out; } if (ip->frag_off & PCKT_FRAGMENTED) { /* drop fragmented packets */ rv = DROP_PACKET; goto out; } __u8 protocol = ip->protocol; if (protocol == IPPROTO_UDP) { /* handle UDP */ struct udphdr *udp = (struct udphdr *)(ip + 1); if (udp + 1 > data_end) { rv = DROP_PACKET; goto out; } if (!allow_udp_pkt_egress(udp)) { rv = DROP_PACKET; goto out; } else { rv = ALLOW_PACKET; goto out; } } else if (protocol == IPPROTO_TCP) { /* handle TCP */ struct tcphdr *tcp = (struct tcphdr *)(ip + 1); if (tcp + 1 > data_end) { rv = DROP_PACKET; goto out; } if (!allow_tcp_pkt_egress(tcp, ip)) { rv = DROP_PACKET; goto out; } else { rv = ALLOW_PACKET; goto out; } } else if (protocol == IPPROTO_ICMP) { /* check IP exceptionlist for ICMP */ if (!allow_destination_IP(ip)) { rv = DROP_PACKET; goto out; } else { rv = ALLOW_PACKET; goto out; } } else { /* drop other protos */ rv = DROP_PACKET; goto out; } out: return rv; } char __license[] __section("license") = "Dual BSD/GPL";