linux/tools/testing/selftests/bpf/test_tcp_estats.c
<<
>>
Prefs
   1/* Copyright (c) 2017 Facebook
   2 *
   3 * This program is free software; you can redistribute it and/or
   4 * modify it under the terms of version 2 of the GNU General Public
   5 * License as published by the Free Software Foundation.
   6 */
   7
   8/* This program shows clang/llvm is able to generate code pattern
   9 * like:
  10 *   _tcp_send_active_reset:
  11 *      0:       bf 16 00 00 00 00 00 00         r6 = r1
  12 *    ......
  13 *    335:       b7 01 00 00 0f 00 00 00         r1 = 15
  14 *    336:       05 00 48 00 00 00 00 00         goto 72
  15 *
  16 *   LBB0_3:
  17 *    337:       b7 01 00 00 01 00 00 00         r1 = 1
  18 *    338:       63 1a d0 ff 00 00 00 00         *(u32 *)(r10 - 48) = r1
  19 *    408:       b7 01 00 00 03 00 00 00         r1 = 3
  20 *
  21 *   LBB0_4:
  22 *    409:       71 a2 fe ff 00 00 00 00         r2 = *(u8 *)(r10 - 2)
  23 *    410:       bf a7 00 00 00 00 00 00         r7 = r10
  24 *    411:       07 07 00 00 b8 ff ff ff         r7 += -72
  25 *    412:       bf 73 00 00 00 00 00 00         r3 = r7
  26 *    413:       0f 13 00 00 00 00 00 00         r3 += r1
  27 *    414:       73 23 2d 00 00 00 00 00         *(u8 *)(r3 + 45) = r2
  28 *
  29 * From the above code snippet, the code generated by the compiler
  30 * is reasonable. The "r1" is assigned to different values in basic
  31 * blocks "_tcp_send_active_reset" and "LBB0_3", and used in "LBB0_4".
  32 * The verifier should be able to handle such code patterns.
  33 */
  34#include <string.h>
  35#include <linux/bpf.h>
  36#include <linux/ipv6.h>
  37#include <linux/version.h>
  38#include <sys/socket.h>
  39#include "bpf_helpers.h"
  40
  41#define _(P) ({typeof(P) val = 0; bpf_probe_read(&val, sizeof(val), &P); val;})
  42#define TCP_ESTATS_MAGIC 0xBAADBEEF
  43
  44/* This test case needs "sock" and "pt_regs" data structure.
  45 * Recursively, "sock" needs "sock_common" and "inet_sock".
  46 * However, this is a unit test case only for
  47 * verifier purpose without bpf program execution.
  48 * We can safely mock much simpler data structures, basically
  49 * only taking the necessary fields from kernel headers.
  50 */
  51typedef __u32 __bitwise __portpair;
  52typedef __u64 __bitwise __addrpair;
  53
  54struct sock_common {
  55        unsigned short          skc_family;
  56        union {
  57                __addrpair      skc_addrpair;
  58                struct {
  59                        __be32  skc_daddr;
  60                        __be32  skc_rcv_saddr;
  61                };
  62        };
  63        union {
  64                __portpair      skc_portpair;
  65                struct {
  66                        __be16  skc_dport;
  67                        __u16   skc_num;
  68                };
  69        };
  70        struct in6_addr         skc_v6_daddr;
  71        struct in6_addr         skc_v6_rcv_saddr;
  72};
  73
  74struct sock {
  75        struct sock_common      __sk_common;
  76#define sk_family               __sk_common.skc_family
  77#define sk_v6_daddr             __sk_common.skc_v6_daddr
  78#define sk_v6_rcv_saddr         __sk_common.skc_v6_rcv_saddr
  79};
  80
  81struct inet_sock {
  82        struct sock             sk;
  83#define inet_daddr              sk.__sk_common.skc_daddr
  84#define inet_dport              sk.__sk_common.skc_dport
  85        __be32                  inet_saddr;
  86        __be16                  inet_sport;
  87};
  88
  89struct pt_regs {
  90        long di;
  91};
  92
  93static inline struct inet_sock *inet_sk(const struct sock *sk)
  94{
  95        return (struct inet_sock *)sk;
  96}
  97
  98/* Define various data structures for state recording.
  99 * Some fields are not used due to test simplification.
 100 */
 101enum tcp_estats_addrtype {
 102        TCP_ESTATS_ADDRTYPE_IPV4 = 1,
 103        TCP_ESTATS_ADDRTYPE_IPV6 = 2
 104};
 105
 106enum tcp_estats_event_type {
 107        TCP_ESTATS_ESTABLISH,
 108        TCP_ESTATS_PERIODIC,
 109        TCP_ESTATS_TIMEOUT,
 110        TCP_ESTATS_RETRANSMIT_TIMEOUT,
 111        TCP_ESTATS_RETRANSMIT_OTHER,
 112        TCP_ESTATS_SYN_RETRANSMIT,
 113        TCP_ESTATS_SYNACK_RETRANSMIT,
 114        TCP_ESTATS_TERM,
 115        TCP_ESTATS_TX_RESET,
 116        TCP_ESTATS_RX_RESET,
 117        TCP_ESTATS_WRITE_TIMEOUT,
 118        TCP_ESTATS_CONN_TIMEOUT,
 119        TCP_ESTATS_ACK_LATENCY,
 120        TCP_ESTATS_NEVENTS,
 121};
 122
 123struct tcp_estats_event {
 124        int pid;
 125        int cpu;
 126        unsigned long ts;
 127        unsigned int magic;
 128        enum tcp_estats_event_type event_type;
 129};
 130
 131/* The below data structure is packed in order for
 132 * llvm compiler to generate expected code.
 133 */
 134struct tcp_estats_conn_id {
 135        unsigned int localaddressType;
 136        struct {
 137                unsigned char data[16];
 138        } localaddress;
 139        struct {
 140                unsigned char data[16];
 141        } remaddress;
 142        unsigned short    localport;
 143        unsigned short    remport;
 144} __attribute__((__packed__));
 145
 146struct tcp_estats_basic_event {
 147        struct tcp_estats_event event;
 148        struct tcp_estats_conn_id conn_id;
 149};
 150
 151struct bpf_map_def SEC("maps") ev_record_map = {
 152        .type = BPF_MAP_TYPE_HASH,
 153        .key_size = sizeof(__u32),
 154        .value_size = sizeof(struct tcp_estats_basic_event),
 155        .max_entries = 1024,
 156};
 157
 158struct dummy_tracepoint_args {
 159        unsigned long long pad;
 160        struct sock *sock;
 161};
 162
 163static __always_inline void tcp_estats_ev_init(struct tcp_estats_event *event,
 164                                               enum tcp_estats_event_type type)
 165{
 166        event->magic = TCP_ESTATS_MAGIC;
 167        event->ts = bpf_ktime_get_ns();
 168        event->event_type = type;
 169}
 170
 171static __always_inline void unaligned_u32_set(unsigned char *to, __u8 *from)
 172{
 173        to[0] = _(from[0]);
 174        to[1] = _(from[1]);
 175        to[2] = _(from[2]);
 176        to[3] = _(from[3]);
 177}
 178
 179static __always_inline void conn_id_ipv4_init(struct tcp_estats_conn_id *conn_id,
 180                                              __be32 *saddr, __be32 *daddr)
 181{
 182        conn_id->localaddressType = TCP_ESTATS_ADDRTYPE_IPV4;
 183
 184        unaligned_u32_set(conn_id->localaddress.data, (__u8 *)saddr);
 185        unaligned_u32_set(conn_id->remaddress.data, (__u8 *)daddr);
 186}
 187
 188static __always_inline void conn_id_ipv6_init(struct tcp_estats_conn_id *conn_id,
 189                                              __be32 *saddr, __be32 *daddr)
 190{
 191        conn_id->localaddressType = TCP_ESTATS_ADDRTYPE_IPV6;
 192
 193        unaligned_u32_set(conn_id->localaddress.data, (__u8 *)saddr);
 194        unaligned_u32_set(conn_id->localaddress.data + sizeof(__u32),
 195                          (__u8 *)(saddr + 1));
 196        unaligned_u32_set(conn_id->localaddress.data + sizeof(__u32) * 2,
 197                          (__u8 *)(saddr + 2));
 198        unaligned_u32_set(conn_id->localaddress.data + sizeof(__u32) * 3,
 199                          (__u8 *)(saddr + 3));
 200
 201        unaligned_u32_set(conn_id->remaddress.data,
 202                          (__u8 *)(daddr));
 203        unaligned_u32_set(conn_id->remaddress.data + sizeof(__u32),
 204                          (__u8 *)(daddr + 1));
 205        unaligned_u32_set(conn_id->remaddress.data + sizeof(__u32) * 2,
 206                          (__u8 *)(daddr + 2));
 207        unaligned_u32_set(conn_id->remaddress.data + sizeof(__u32) * 3,
 208                          (__u8 *)(daddr + 3));
 209}
 210
 211static __always_inline void tcp_estats_conn_id_init(struct tcp_estats_conn_id *conn_id,
 212                                                    struct sock *sk)
 213{
 214        conn_id->localport = _(inet_sk(sk)->inet_sport);
 215        conn_id->remport = _(inet_sk(sk)->inet_dport);
 216
 217        if (_(sk->sk_family) == AF_INET6)
 218                conn_id_ipv6_init(conn_id,
 219                                  sk->sk_v6_rcv_saddr.s6_addr32,
 220                                  sk->sk_v6_daddr.s6_addr32);
 221        else
 222                conn_id_ipv4_init(conn_id,
 223                                  &inet_sk(sk)->inet_saddr,
 224                                  &inet_sk(sk)->inet_daddr);
 225}
 226
 227static __always_inline void tcp_estats_init(struct sock *sk,
 228                                            struct tcp_estats_event *event,
 229                                            struct tcp_estats_conn_id *conn_id,
 230                                            enum tcp_estats_event_type type)
 231{
 232        tcp_estats_ev_init(event, type);
 233        tcp_estats_conn_id_init(conn_id, sk);
 234}
 235
 236static __always_inline void send_basic_event(struct sock *sk,
 237                                             enum tcp_estats_event_type type)
 238{
 239        struct tcp_estats_basic_event ev;
 240        __u32 key = bpf_get_prandom_u32();
 241
 242        memset(&ev, 0, sizeof(ev));
 243        tcp_estats_init(sk, &ev.event, &ev.conn_id, type);
 244        bpf_map_update_elem(&ev_record_map, &key, &ev, BPF_ANY);
 245}
 246
 247SEC("dummy_tracepoint")
 248int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
 249{
 250        if (!arg->sock)
 251                return 0;
 252
 253        send_basic_event(arg->sock, TCP_ESTATS_TX_RESET);
 254        return 0;
 255}
 256
 257char _license[] SEC("license") = "GPL";
 258__u32 _version SEC("version") = 1; /* ignored by tracepoints, required by libbpf.a */
 259