/* Copyright (c) 2017 Facebook * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. */ /* This program shows clang/llvm is able to generate code pattern * like: * _tcp_send_active_reset: * 0: bf 16 00 00 00 00 00 00 r6 = r1 * ...... * 335: b7 01 00 00 0f 00 00 00 r1 = 15 * 336: 05 00 48 00 00 00 00 00 goto 72 * * LBB0_3: * 337: b7 01 00 00 01 00 00 00 r1 = 1 * 338: 63 1a d0 ff 00 00 00 00 *(u32 *)(r10 - 48) = r1 * 408: b7 01 00 00 03 00 00 00 r1 = 3 * * LBB0_4: * 409: 71 a2 fe ff 00 00 00 00 r2 = *(u8 *)(r10 - 2) * 410: bf a7 00 00 00 00 00 00 r7 = r10 * 411: 07 07 00 00 b8 ff ff ff r7 += -72 * 412: bf 73 00 00 00 00 00 00 r3 = r7 * 413: 0f 13 00 00 00 00 00 00 r3 += r1 * 414: 73 23 2d 00 00 00 00 00 *(u8 *)(r3 + 45) = r2 * * From the above code snippet, the code generated by the compiler * is reasonable. The "r1" is assigned to different values in basic * blocks "_tcp_send_active_reset" and "LBB0_3", and used in "LBB0_4". * The verifier should be able to handle such code patterns. */ #include <string.h> #include <linux/bpf.h> #include <linux/ipv6.h> #include <linux/version.h> #include <sys/socket.h> #include <bpf/bpf_helpers.h> #define _(P) ({typeof(P) val = 0; bpf_probe_read_kernel(&val, sizeof(val), &P); val;}) #define TCP_ESTATS_MAGIC 0xBAADBEEF /* This test case needs "sock" and "pt_regs" data structure. * Recursively, "sock" needs "sock_common" and "inet_sock". * However, this is a unit test case only for * verifier purpose without bpf program execution. * We can safely mock much simpler data structures, basically * only taking the necessary fields from kernel headers. */ typedef __u32 __bitwise __portpair; typedef __u64 __bitwise __addrpair; struct sock_common { unsigned short skc_family; union { __addrpair skc_addrpair; struct { __be32 skc_daddr; __be32 skc_rcv_saddr; }; }; union { __portpair skc_portpair; struct { __be16 skc_dport; __u16 skc_num; }; }; struct in6_addr skc_v6_daddr; struct in6_addr skc_v6_rcv_saddr; }; struct sock { struct sock_common __sk_common; #define sk_family __sk_common.skc_family #define sk_v6_daddr __sk_common.skc_v6_daddr #define sk_v6_rcv_saddr __sk_common.skc_v6_rcv_saddr }; struct inet_sock { struct sock sk; #define inet_daddr sk.__sk_common.skc_daddr #define inet_dport sk.__sk_common.skc_dport __be32 inet_saddr; __be16 inet_sport; }; struct pt_regs { long di; }; static inline struct inet_sock *inet_sk(const struct sock *sk) { return (struct inet_sock *)sk; } /* Define various data structures for state recording. * Some fields are not used due to test simplification. */ enum tcp_estats_addrtype { TCP_ESTATS_ADDRTYPE_IPV4 = 1, TCP_ESTATS_ADDRTYPE_IPV6 = 2 }; enum tcp_estats_event_type { TCP_ESTATS_ESTABLISH, TCP_ESTATS_PERIODIC, TCP_ESTATS_TIMEOUT, TCP_ESTATS_RETRANSMIT_TIMEOUT, TCP_ESTATS_RETRANSMIT_OTHER, TCP_ESTATS_SYN_RETRANSMIT, TCP_ESTATS_SYNACK_RETRANSMIT, TCP_ESTATS_TERM, TCP_ESTATS_TX_RESET, TCP_ESTATS_RX_RESET, TCP_ESTATS_WRITE_TIMEOUT, TCP_ESTATS_CONN_TIMEOUT, TCP_ESTATS_ACK_LATENCY, TCP_ESTATS_NEVENTS, }; struct tcp_estats_event { int pid; int cpu; unsigned long ts; unsigned int magic; enum tcp_estats_event_type event_type; }; /* The below data structure is packed in order for * llvm compiler to generate expected code. */ struct tcp_estats_conn_id { unsigned int localaddressType; struct { unsigned char data[16]; } localaddress; struct { unsigned char data[16]; } remaddress; unsigned short localport; unsigned short remport; } __attribute__((__packed__)); struct tcp_estats_basic_event { struct tcp_estats_event event; struct tcp_estats_conn_id conn_id; }; struct { __uint(type, BPF_MAP_TYPE_HASH); __uint(max_entries, 1024); __type(key, __u32); __type(value, struct tcp_estats_basic_event); } ev_record_map SEC(".maps"); struct dummy_tracepoint_args { unsigned long long pad; struct sock *sock; }; static __always_inline void tcp_estats_ev_init(struct tcp_estats_event *event, enum tcp_estats_event_type type) { event->magic = TCP_ESTATS_MAGIC; event->ts = bpf_ktime_get_ns(); event->event_type = type; } static __always_inline void unaligned_u32_set(unsigned char *to, __u8 *from) { to[0] = _(from[0]); to[1] = _(from[1]); to[2] = _(from[2]); to[3] = _(from[3]); } static __always_inline void conn_id_ipv4_init(struct tcp_estats_conn_id *conn_id, __be32 *saddr, __be32 *daddr) { conn_id->localaddressType = TCP_ESTATS_ADDRTYPE_IPV4; unaligned_u32_set(conn_id->localaddress.data, (__u8 *)saddr); unaligned_u32_set(conn_id->remaddress.data, (__u8 *)daddr); } static __always_inline void conn_id_ipv6_init(struct tcp_estats_conn_id *conn_id, __be32 *saddr, __be32 *daddr) { conn_id->localaddressType = TCP_ESTATS_ADDRTYPE_IPV6; unaligned_u32_set(conn_id->localaddress.data, (__u8 *)saddr); unaligned_u32_set(conn_id->localaddress.data + sizeof(__u32), (__u8 *)(saddr + 1)); unaligned_u32_set(conn_id->localaddress.data + sizeof(__u32) * 2, (__u8 *)(saddr + 2)); unaligned_u32_set(conn_id->localaddress.data + sizeof(__u32) * 3, (__u8 *)(saddr + 3)); unaligned_u32_set(conn_id->remaddress.data, (__u8 *)(daddr)); unaligned_u32_set(conn_id->remaddress.data + sizeof(__u32), (__u8 *)(daddr + 1)); unaligned_u32_set(conn_id->remaddress.data + sizeof(__u32) * 2, (__u8 *)(daddr + 2)); unaligned_u32_set(conn_id->remaddress.data + sizeof(__u32) * 3, (__u8 *)(daddr + 3)); } static __always_inline void tcp_estats_conn_id_init(struct tcp_estats_conn_id *conn_id, struct sock *sk) { conn_id->localport = _(inet_sk(sk)->inet_sport); conn_id->remport = _(inet_sk(sk)->inet_dport); if (_(sk->sk_family) == AF_INET6) conn_id_ipv6_init(conn_id, sk->sk_v6_rcv_saddr.s6_addr32, sk->sk_v6_daddr.s6_addr32); else conn_id_ipv4_init(conn_id, &inet_sk(sk)->inet_saddr, &inet_sk(sk)->inet_daddr); } static __always_inline void tcp_estats_init(struct sock *sk, struct tcp_estats_event *event, struct tcp_estats_conn_id *conn_id, enum tcp_estats_event_type type) { tcp_estats_ev_init(event, type); tcp_estats_conn_id_init(conn_id, sk); } static __always_inline void send_basic_event(struct sock *sk, enum tcp_estats_event_type type) { struct tcp_estats_basic_event ev; __u32 key = bpf_get_prandom_u32(); memset(&ev, 0, sizeof(ev)); tcp_estats_init(sk, &ev.event, &ev.conn_id, type); bpf_map_update_elem(&ev_record_map, &key, &ev, BPF_ANY); } SEC("tp/dummy/tracepoint") int _dummy_tracepoint(struct dummy_tracepoint_args *arg) { if (!arg->sock) return 0; send_basic_event(arg->sock, TCP_ESTATS_TX_RESET); return 0; } char _license[] SEC("license") = "GPL";