1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2019 Facebook */
5 #include <netinet/in.h>
8 #include <bpf/bpf_helpers.h>
9 #include <bpf/bpf_endian.h>
11 enum bpf_linum_array_idx {
14 __NR_BPF_LINUM_ARRAY_IDX,
18 __uint(type, BPF_MAP_TYPE_ARRAY);
19 __uint(max_entries, __NR_BPF_LINUM_ARRAY_IDX);
22 } linum_map SEC(".maps");
24 struct bpf_spinlock_cnt {
25 struct bpf_spin_lock lock;
30 __uint(type, BPF_MAP_TYPE_SK_STORAGE);
31 __uint(map_flags, BPF_F_NO_PREALLOC);
33 __type(value, struct bpf_spinlock_cnt);
34 } sk_pkt_out_cnt SEC(".maps");
37 __uint(type, BPF_MAP_TYPE_SK_STORAGE);
38 __uint(map_flags, BPF_F_NO_PREALLOC);
40 __type(value, struct bpf_spinlock_cnt);
41 } sk_pkt_out_cnt10 SEC(".maps");
43 struct bpf_tcp_sock listen_tp = {};
44 struct sockaddr_in6 srv_sa6 = {};
45 struct bpf_tcp_sock cli_tp = {};
46 struct bpf_tcp_sock srv_tp = {};
47 struct bpf_sock listen_sk = {};
48 struct bpf_sock srv_sk = {};
49 struct bpf_sock cli_sk = {};
51 static bool is_loopback6(__u32 *a6)
53 return !a6[0] && !a6[1] && !a6[2] && a6[3] == bpf_htonl(1);
56 static void skcpy(struct bpf_sock *dst,
57 const struct bpf_sock *src)
59 dst->bound_dev_if = src->bound_dev_if;
60 dst->family = src->family;
61 dst->type = src->type;
62 dst->protocol = src->protocol;
63 dst->mark = src->mark;
64 dst->priority = src->priority;
65 dst->src_ip4 = src->src_ip4;
66 dst->src_ip6[0] = src->src_ip6[0];
67 dst->src_ip6[1] = src->src_ip6[1];
68 dst->src_ip6[2] = src->src_ip6[2];
69 dst->src_ip6[3] = src->src_ip6[3];
70 dst->src_port = src->src_port;
71 dst->dst_ip4 = src->dst_ip4;
72 dst->dst_ip6[0] = src->dst_ip6[0];
73 dst->dst_ip6[1] = src->dst_ip6[1];
74 dst->dst_ip6[2] = src->dst_ip6[2];
75 dst->dst_ip6[3] = src->dst_ip6[3];
76 dst->dst_port = src->dst_port;
77 dst->state = src->state;
80 static void tpcpy(struct bpf_tcp_sock *dst,
81 const struct bpf_tcp_sock *src)
83 dst->snd_cwnd = src->snd_cwnd;
84 dst->srtt_us = src->srtt_us;
85 dst->rtt_min = src->rtt_min;
86 dst->snd_ssthresh = src->snd_ssthresh;
87 dst->rcv_nxt = src->rcv_nxt;
88 dst->snd_nxt = src->snd_nxt;
89 dst->snd_una = src->snd_una;
90 dst->mss_cache = src->mss_cache;
91 dst->ecn_flags = src->ecn_flags;
92 dst->rate_delivered = src->rate_delivered;
93 dst->rate_interval_us = src->rate_interval_us;
94 dst->packets_out = src->packets_out;
95 dst->retrans_out = src->retrans_out;
96 dst->total_retrans = src->total_retrans;
97 dst->segs_in = src->segs_in;
98 dst->data_segs_in = src->data_segs_in;
99 dst->segs_out = src->segs_out;
100 dst->data_segs_out = src->data_segs_out;
101 dst->lost_out = src->lost_out;
102 dst->sacked_out = src->sacked_out;
103 dst->bytes_received = src->bytes_received;
104 dst->bytes_acked = src->bytes_acked;
107 /* Always return CG_OK so that no pkt will be filtered out */
110 #define RET_LOG() ({ \
112 bpf_map_update_elem(&linum_map, &linum_idx, &linum, BPF_NOEXIST); \
116 SEC("cgroup_skb/egress")
117 int egress_read_sock_fields(struct __sk_buff *skb)
119 struct bpf_spinlock_cnt cli_cnt_init = { .lock = 0, .cnt = 0xeB9F };
120 struct bpf_spinlock_cnt *pkt_out_cnt, *pkt_out_cnt10;
121 struct bpf_tcp_sock *tp, *tp_ret;
122 struct bpf_sock *sk, *sk_ret;
123 __u32 linum, linum_idx;
125 linum_idx = EGRESS_LINUM_IDX;
131 /* Not the testing egress traffic or
132 * TCP_LISTEN (10) socket will be copied at the ingress side.
134 if (sk->family != AF_INET6 || !is_loopback6(sk->src_ip6) ||
138 if (sk->src_port == bpf_ntohs(srv_sa6.sin6_port)) {
142 } else if (sk->dst_port == srv_sa6.sin6_port) {
147 /* Not the testing egress traffic */
151 /* It must be a fullsock for cgroup_skb/egress prog */
152 sk = bpf_sk_fullsock(sk);
156 /* Not the testing egress traffic */
157 if (sk->protocol != IPPROTO_TCP)
160 tp = bpf_tcp_sock(sk);
167 if (sk_ret == &srv_sk) {
168 /* The userspace has created it for srv sk */
169 pkt_out_cnt = bpf_sk_storage_get(&sk_pkt_out_cnt, sk, 0, 0);
170 pkt_out_cnt10 = bpf_sk_storage_get(&sk_pkt_out_cnt10, sk,
173 pkt_out_cnt = bpf_sk_storage_get(&sk_pkt_out_cnt, sk,
175 BPF_SK_STORAGE_GET_F_CREATE);
176 pkt_out_cnt10 = bpf_sk_storage_get(&sk_pkt_out_cnt10,
178 BPF_SK_STORAGE_GET_F_CREATE);
181 if (!pkt_out_cnt || !pkt_out_cnt10)
184 /* Even both cnt and cnt10 have lock defined in their BTF,
185 * intentionally one cnt takes lock while one does not
186 * as a test for the spinlock support in BPF_MAP_TYPE_SK_STORAGE.
188 pkt_out_cnt->cnt += 1;
189 bpf_spin_lock(&pkt_out_cnt10->lock);
190 pkt_out_cnt10->cnt += 10;
191 bpf_spin_unlock(&pkt_out_cnt10->lock);
196 SEC("cgroup_skb/ingress")
197 int ingress_read_sock_fields(struct __sk_buff *skb)
199 struct bpf_tcp_sock *tp;
200 __u32 linum, linum_idx;
203 linum_idx = INGRESS_LINUM_IDX;
209 /* Not the testing ingress traffic to the server */
210 if (sk->family != AF_INET6 || !is_loopback6(sk->src_ip6) ||
211 sk->src_port != bpf_ntohs(srv_sa6.sin6_port))
214 /* Only interested in TCP_LISTEN */
218 /* It must be a fullsock for cgroup_skb/ingress prog */
219 sk = bpf_sk_fullsock(sk);
223 tp = bpf_tcp_sock(sk);
227 skcpy(&listen_sk, sk);
228 tpcpy(&listen_tp, tp);
233 char _license[] SEC("license") = "GPL";