Created
November 3, 2017 18:07
-
-
Save mpurzynski/8c52434aa21516f5a56de1d4cef8170e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //#include <bcc/proto.h> | |
| #define KBUILD_MODNAME "foo" | |
| #include <stdint.h> | |
| #include <string.h> | |
| #include <stddef.h> | |
| #include <linux/bpf.h> | |
| #include <linux/in.h> | |
| #include <linux/if_ether.h> | |
| #include <linux/if_packet.h> | |
| #include <linux/if_vlan.h> | |
| #include <linux/ip.h> | |
| #include <linux/ipv6.h> | |
| #include <linux/tcp.h> | |
| #include <linux/udp.h> | |
| #include "bpf_helpers.h" | |
| #define LINUX_VERSION_CODE 263682 | |
| struct vlan_hdr { | |
| __u16 h_vlan_TCI; | |
| __u16 h_vlan_encapsulated_proto; | |
| }; | |
| struct flowv4_keys { | |
| __u32 src; | |
| __u32 dst; | |
| union { | |
| __u32 ports; | |
| __u16 port16[2]; | |
| }; | |
| __u32 ip_proto; | |
| }; | |
| struct flowv6_keys { | |
| __u32 src[4]; | |
| __u32 dst[4]; | |
| union { | |
| __u32 ports; | |
| __u16 port16[2]; | |
| }; | |
| __u32 ip_proto; | |
| }; | |
| struct pair { | |
| uint64_t time; | |
| uint64_t packets; | |
| uint64_t bytes; | |
| }; | |
| struct bpf_map_def SEC("maps") flow_table_v4 = { | |
| .type = BPF_MAP_TYPE_HASH, | |
| .key_size = sizeof(struct flowv4_keys), | |
| .value_size = sizeof(struct pair), | |
| .max_entries = 32768, | |
| }; | |
| struct bpf_map_def SEC("maps") flow_table_v6 = { | |
| .type = BPF_MAP_TYPE_HASH, | |
| .key_size = sizeof(struct flowv6_keys), | |
| .value_size = sizeof(struct pair), | |
| .max_entries = 32768, | |
| }; | |
| static __always_inline int get_sport(void *trans_data, void *data_end, | |
| uint8_t protocol) | |
| { | |
| struct tcphdr *th; | |
| struct udphdr *uh; | |
| switch (protocol) { | |
| case IPPROTO_TCP: | |
| th = (struct tcphdr *)trans_data; | |
| if ((void *)(th + 1) > data_end) | |
| return -1; | |
| return th->source; | |
| case IPPROTO_UDP: | |
| uh = (struct udphdr *)trans_data; | |
| if ((void *)(uh + 1) > data_end) | |
| return -1; | |
| return uh->dest; | |
| default: | |
| return 0; | |
| } | |
| } | |
| static __always_inline int get_dport(void *trans_data, void *data_end, | |
| uint8_t protocol) | |
| { | |
| struct tcphdr *th; | |
| struct udphdr *uh; | |
| switch (protocol) { | |
| case IPPROTO_TCP: | |
| th = (struct tcphdr *)trans_data; | |
| if ((void *)(th + 1) > data_end) | |
| return -1; | |
| return th->dest; | |
| case IPPROTO_UDP: | |
| uh = (struct udphdr *)trans_data; | |
| if ((void *)(uh + 1) > data_end) | |
| return -1; | |
| return uh->dest; | |
| default: | |
| return 0; | |
| } | |
| } | |
| static int filter_ipv4(void *data, __u64 nh_off, void *data_end) | |
| { | |
| struct iphdr *iph = data + nh_off; | |
| int dport; | |
| int sport; | |
| struct flowv4_keys tuple; | |
| struct pair *value; | |
| if ((void *)(iph + 1) > data_end) | |
| return 0; | |
| tuple.ip_proto = (uint32_t) iph->protocol; | |
| tuple.src = iph->saddr; | |
| tuple.dst = iph->daddr; | |
| dport = get_dport(iph + 1, data_end, iph->protocol); | |
| if (dport == -1) | |
| return XDP_PASS; | |
| sport = get_sport(iph + 1, data_end, iph->protocol); | |
| if (sport == -1) | |
| return XDP_PASS; | |
| tuple.port16[0] = (uint16_t)sport; | |
| tuple.port16[1] = (uint16_t)dport; | |
| value = bpf_map_lookup_elem(&flow_table_v4, &tuple); | |
| #if 0 | |
| { | |
| char fmt[] = "Current flow src: %u:%d\n"; | |
| char fmt1[] = "Current flow dst: %u:%d\n"; | |
| bpf_trace_printk(fmt, sizeof(fmt), tuple.src, tuple.port16[0]); | |
| bpf_trace_printk(fmt1, sizeof(fmt1), tuple.dst, tuple.port16[1]); | |
| } | |
| #endif | |
| if (value) { | |
| #if 0 | |
| char fmt[] = "Found flow v4: %u %d -> %d\n"; | |
| bpf_trace_printk(fmt, sizeof(fmt), tuple.src, sport, dport); | |
| #endif | |
| __sync_fetch_and_add(&value->packets, 1); | |
| __sync_fetch_and_add(&value->bytes, data_end - data); | |
| value->time = bpf_ktime_get_ns(); | |
| return XDP_DROP; | |
| } | |
| return XDP_PASS; | |
| } | |
| static int filter_ipv6(void *data, __u64 nh_off, void *data_end) | |
| { | |
| struct ipv6hdr *ip6h = data + nh_off; | |
| int dport; | |
| int sport; | |
| struct flowv6_keys tuple; | |
| struct pair *value; | |
| if ((void *)(ip6h + 1) > data_end) | |
| return 0; | |
| if (!((ip6h->nexthdr == IPPROTO_UDP) || (ip6h->nexthdr == IPPROTO_TCP))) | |
| return XDP_PASS; | |
| dport = get_dport(ip6h + 1, data_end, ip6h->nexthdr); | |
| if (dport == -1) | |
| return XDP_PASS; | |
| sport = get_sport(ip6h + 1, data_end, ip6h->nexthdr); | |
| if (sport == -1) | |
| return XDP_PASS; | |
| tuple.ip_proto = ip6h->nexthdr; | |
| __builtin_memcpy(tuple.src, ip6h->saddr.s6_addr32, sizeof(tuple.src)); | |
| __builtin_memcpy(tuple.dst, ip6h->daddr.s6_addr32, sizeof(tuple.dst)); | |
| tuple.port16[0] = sport; | |
| tuple.port16[1] = dport; | |
| value = bpf_map_lookup_elem(&flow_table_v6, &tuple); | |
| if (value) { | |
| #if 0 | |
| char fmt6[] = "Found IPv6 flow: %d -> %d\n"; | |
| bpf_trace_printk(fmt6, sizeof(fmt6), sport, dport); | |
| #endif | |
| __sync_fetch_and_add(&value->packets, 1); | |
| __sync_fetch_and_add(&value->bytes, data_end - data); | |
| value->time = bpf_ktime_get_ns(); | |
| return XDP_DROP; | |
| } | |
| return XDP_PASS; | |
| } | |
| int SEC("xdp") xdp_hashfilter(struct xdp_md *ctx) | |
| { | |
| void *data_end = (void *)(long)ctx->data_end; | |
| void *data = (void *)(long)ctx->data; | |
| struct ethhdr *eth = data; | |
| int rc = XDP_PASS; | |
| uint16_t h_proto; | |
| uint64_t nh_off; | |
| nh_off = sizeof(*eth); | |
| if (data + nh_off > data_end) | |
| return rc; | |
| h_proto = eth->h_proto; | |
| if (h_proto == __constant_htons(ETH_P_8021Q) || h_proto == __constant_htons(ETH_P_8021AD)) { | |
| struct vlan_hdr *vhdr; | |
| vhdr = data + nh_off; | |
| nh_off += sizeof(struct vlan_hdr); | |
| if (data + nh_off > data_end) | |
| return rc; | |
| h_proto = vhdr->h_vlan_encapsulated_proto; | |
| } | |
| if (h_proto == __constant_htons(ETH_P_8021Q) || h_proto == __constant_htons(ETH_P_8021AD)) { | |
| struct vlan_hdr *vhdr; | |
| vhdr = data + nh_off; | |
| nh_off += sizeof(struct vlan_hdr); | |
| if (data + nh_off > data_end) | |
| return rc; | |
| h_proto = vhdr->h_vlan_encapsulated_proto; | |
| } | |
| if (h_proto == __constant_htons(ETH_P_IP)) | |
| return filter_ipv4(data, nh_off, data_end); | |
| else if (h_proto == __constant_htons(ETH_P_IPV6)) | |
| return filter_ipv6(data, nh_off, data_end); | |
| else | |
| rc = XDP_PASS; | |
| return rc; | |
| } | |
| char __license[] SEC("license") = "GPL"; | |
| uint32_t __version SEC("version") = LINUX_VERSION_CODE; | |
| root@nsm16:/home/mpurzynski/suricata_source/suricata# |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment