258 lines
7.0 KiB
C
258 lines
7.0 KiB
C
|
/* 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";
|