root/tools/testing/selftests/bpf/test_tcp_hdr_options.h
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2020 Facebook */

#ifndef _TEST_TCP_HDR_OPTIONS_H
#define _TEST_TCP_HDR_OPTIONS_H

struct bpf_test_option {
        __u8 flags;
        __u8 max_delack_ms;
        __u8 rand;
} __attribute__((packed));

enum {
        OPTION_RESEND,
        OPTION_MAX_DELACK_MS,
        OPTION_RAND,
        __NR_OPTION_FLAGS,
};

#define OPTION_F_RESEND         (1 << OPTION_RESEND)
#define OPTION_F_MAX_DELACK_MS  (1 << OPTION_MAX_DELACK_MS)
#define OPTION_F_RAND           (1 << OPTION_RAND)
#define OPTION_MASK             ((1 << __NR_OPTION_FLAGS) - 1)

#define TEST_OPTION_FLAGS(flags, option) (1 & ((flags) >> (option)))
#define SET_OPTION_FLAGS(flags, option) ((flags) |= (1 << (option)))

/* Store in bpf_sk_storage */
struct hdr_stg {
        bool active;
        bool resend_syn; /* active side only */
        bool syncookie;  /* passive side only */
        bool fastopen;  /* passive side only */
};

struct linum_err {
        unsigned int linum;
        int err;
};

#define TCPHDR_FIN 0x01
#define TCPHDR_SYN 0x02
#define TCPHDR_RST 0x04
#define TCPHDR_PSH 0x08
#define TCPHDR_ACK 0x10
#define TCPHDR_URG 0x20
#define TCPHDR_ECE 0x40
#define TCPHDR_CWR 0x80
#define TCPHDR_SYNACK (TCPHDR_SYN | TCPHDR_ACK)

#define TCPOPT_EOL              0
#define TCPOPT_NOP              1
#define TCPOPT_MSS              2
#define TCPOPT_WINDOW           3
#define TCPOPT_EXP              254

#define TCP_BPF_EXPOPT_BASE_LEN 4
#define MAX_TCP_HDR_LEN         60
#define MAX_TCP_OPTION_SPACE    40

#ifdef BPF_PROG_TEST_TCP_HDR_OPTIONS

#define CG_OK   1
#define CG_ERR  0

#ifndef SOL_TCP
#define SOL_TCP 6
#endif

struct tcp_exprm_opt {
        __u8 kind;
        __u8 len;
        __u16 magic;
        union {
                __u8 data[4];
                __u32 data32;
        };
} __attribute__((packed));

struct tcp_opt {
        __u8 kind;
        __u8 len;
        union {
                __u8 data[4];
                __u32 data32;
        };
} __attribute__((packed));

struct {
        __uint(type, BPF_MAP_TYPE_HASH);
        __uint(max_entries, 2);
        __type(key, int);
        __type(value, struct linum_err);
} lport_linum_map SEC(".maps");

static inline unsigned int tcp_hdrlen(const struct tcphdr *th)
{
        return th->doff << 2;
}

static inline __u8 skops_tcp_flags(const struct bpf_sock_ops *skops)
{
        return skops->skb_tcp_flags;
}

static inline void clear_hdr_cb_flags(struct bpf_sock_ops *skops)
{
        bpf_sock_ops_cb_flags_set(skops,
                                  skops->bpf_sock_ops_cb_flags &
                                  ~(BPF_SOCK_OPS_PARSE_UNKNOWN_HDR_OPT_CB_FLAG |
                                    BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG));
}

static inline void set_hdr_cb_flags(struct bpf_sock_ops *skops, __u32 extra)
{
        bpf_sock_ops_cb_flags_set(skops,
                                  skops->bpf_sock_ops_cb_flags |
                                  BPF_SOCK_OPS_PARSE_UNKNOWN_HDR_OPT_CB_FLAG |
                                  BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG |
                                  extra);
}
static inline void
clear_parse_all_hdr_cb_flags(struct bpf_sock_ops *skops)
{
        bpf_sock_ops_cb_flags_set(skops,
                                  skops->bpf_sock_ops_cb_flags &
                                  ~BPF_SOCK_OPS_PARSE_ALL_HDR_OPT_CB_FLAG);
}

static inline void
set_parse_all_hdr_cb_flags(struct bpf_sock_ops *skops)
{
        bpf_sock_ops_cb_flags_set(skops,
                                  skops->bpf_sock_ops_cb_flags |
                                  BPF_SOCK_OPS_PARSE_ALL_HDR_OPT_CB_FLAG);
}

#define RET_CG_ERR(__err) ({                    \
        struct linum_err __linum_err;           \
        int __lport;                            \
                                                \
        __linum_err.linum = __LINE__;           \
        __linum_err.err = __err;                \
        __lport = skops->local_port;            \
        bpf_map_update_elem(&lport_linum_map, &__lport, &__linum_err, BPF_NOEXIST); \
        clear_hdr_cb_flags(skops);                                      \
        clear_parse_all_hdr_cb_flags(skops);                            \
        return CG_ERR;                                                  \
})

#endif /* BPF_PROG_TEST_TCP_HDR_OPTIONS */

#endif /* _TEST_TCP_HDR_OPTIONS_H */