16f521a2bSMartin KaFai Lau // SPDX-License-Identifier: GPL-2.0
26f521a2bSMartin KaFai Lau /* Copyright (c) 2019 Facebook */
36f521a2bSMartin KaFai Lau 
46f521a2bSMartin KaFai Lau #include <linux/bpf.h>
56f521a2bSMartin KaFai Lau #include <netinet/in.h>
66f521a2bSMartin KaFai Lau #include <stdbool.h>
76f521a2bSMartin KaFai Lau 
86f521a2bSMartin KaFai Lau #include <bpf/bpf_helpers.h>
96f521a2bSMartin KaFai Lau #include <bpf/bpf_endian.h>
10edc2d66aSMartin KaFai Lau #include "bpf_tcp_helpers.h"
116f521a2bSMartin KaFai Lau 
126f521a2bSMartin KaFai Lau enum bpf_linum_array_idx {
136f521a2bSMartin KaFai Lau 	EGRESS_LINUM_IDX,
146f521a2bSMartin KaFai Lau 	INGRESS_LINUM_IDX,
158f50f16fSJakub Sitnicki 	READ_SK_DST_PORT_LINUM_IDX,
166f521a2bSMartin KaFai Lau 	__NR_BPF_LINUM_ARRAY_IDX,
176f521a2bSMartin KaFai Lau };
186f521a2bSMartin KaFai Lau 
196f521a2bSMartin KaFai Lau struct {
206f521a2bSMartin KaFai Lau 	__uint(type, BPF_MAP_TYPE_ARRAY);
216f521a2bSMartin KaFai Lau 	__uint(max_entries, __NR_BPF_LINUM_ARRAY_IDX);
226f521a2bSMartin KaFai Lau 	__type(key, __u32);
236f521a2bSMartin KaFai Lau 	__type(value, __u32);
246f521a2bSMartin KaFai Lau } linum_map SEC(".maps");
256f521a2bSMartin KaFai Lau 
266f521a2bSMartin KaFai Lau struct bpf_spinlock_cnt {
276f521a2bSMartin KaFai Lau 	struct bpf_spin_lock lock;
286f521a2bSMartin KaFai Lau 	__u32 cnt;
296f521a2bSMartin KaFai Lau };
306f521a2bSMartin KaFai Lau 
316f521a2bSMartin KaFai Lau struct {
326f521a2bSMartin KaFai Lau 	__uint(type, BPF_MAP_TYPE_SK_STORAGE);
336f521a2bSMartin KaFai Lau 	__uint(map_flags, BPF_F_NO_PREALLOC);
346f521a2bSMartin KaFai Lau 	__type(key, int);
356f521a2bSMartin KaFai Lau 	__type(value, struct bpf_spinlock_cnt);
366f521a2bSMartin KaFai Lau } sk_pkt_out_cnt SEC(".maps");
376f521a2bSMartin KaFai Lau 
386f521a2bSMartin KaFai Lau struct {
396f521a2bSMartin KaFai Lau 	__uint(type, BPF_MAP_TYPE_SK_STORAGE);
406f521a2bSMartin KaFai Lau 	__uint(map_flags, BPF_F_NO_PREALLOC);
416f521a2bSMartin KaFai Lau 	__type(key, int);
426f521a2bSMartin KaFai Lau 	__type(value, struct bpf_spinlock_cnt);
436f521a2bSMartin KaFai Lau } sk_pkt_out_cnt10 SEC(".maps");
446f521a2bSMartin KaFai Lau 
45b18c1f0aSMartin KaFai Lau struct bpf_tcp_sock listen_tp = {};
46b18c1f0aSMartin KaFai Lau struct sockaddr_in6 srv_sa6 = {};
47b18c1f0aSMartin KaFai Lau struct bpf_tcp_sock cli_tp = {};
48b18c1f0aSMartin KaFai Lau struct bpf_tcp_sock srv_tp = {};
49b18c1f0aSMartin KaFai Lau struct bpf_sock listen_sk = {};
50b18c1f0aSMartin KaFai Lau struct bpf_sock srv_sk = {};
51b18c1f0aSMartin KaFai Lau struct bpf_sock cli_sk = {};
52edc2d66aSMartin KaFai Lau __u64 parent_cg_id = 0;
53edc2d66aSMartin KaFai Lau __u64 child_cg_id = 0;
54edc2d66aSMartin KaFai Lau __u64 lsndtime = 0;
55b18c1f0aSMartin KaFai Lau 
is_loopback6(__u32 * a6)566f521a2bSMartin KaFai Lau static bool is_loopback6(__u32 *a6)
576f521a2bSMartin KaFai Lau {
586f521a2bSMartin KaFai Lau 	return !a6[0] && !a6[1] && !a6[2] && a6[3] == bpf_htonl(1);
596f521a2bSMartin KaFai Lau }
606f521a2bSMartin KaFai Lau 
skcpy(struct bpf_sock * dst,const struct bpf_sock * src)616f521a2bSMartin KaFai Lau static void skcpy(struct bpf_sock *dst,
626f521a2bSMartin KaFai Lau 		  const struct bpf_sock *src)
636f521a2bSMartin KaFai Lau {
646f521a2bSMartin KaFai Lau 	dst->bound_dev_if = src->bound_dev_if;
656f521a2bSMartin KaFai Lau 	dst->family = src->family;
666f521a2bSMartin KaFai Lau 	dst->type = src->type;
676f521a2bSMartin KaFai Lau 	dst->protocol = src->protocol;
686f521a2bSMartin KaFai Lau 	dst->mark = src->mark;
696f521a2bSMartin KaFai Lau 	dst->priority = src->priority;
706f521a2bSMartin KaFai Lau 	dst->src_ip4 = src->src_ip4;
716f521a2bSMartin KaFai Lau 	dst->src_ip6[0] = src->src_ip6[0];
726f521a2bSMartin KaFai Lau 	dst->src_ip6[1] = src->src_ip6[1];
736f521a2bSMartin KaFai Lau 	dst->src_ip6[2] = src->src_ip6[2];
746f521a2bSMartin KaFai Lau 	dst->src_ip6[3] = src->src_ip6[3];
756f521a2bSMartin KaFai Lau 	dst->src_port = src->src_port;
766f521a2bSMartin KaFai Lau 	dst->dst_ip4 = src->dst_ip4;
776f521a2bSMartin KaFai Lau 	dst->dst_ip6[0] = src->dst_ip6[0];
786f521a2bSMartin KaFai Lau 	dst->dst_ip6[1] = src->dst_ip6[1];
796f521a2bSMartin KaFai Lau 	dst->dst_ip6[2] = src->dst_ip6[2];
806f521a2bSMartin KaFai Lau 	dst->dst_ip6[3] = src->dst_ip6[3];
816f521a2bSMartin KaFai Lau 	dst->dst_port = src->dst_port;
826f521a2bSMartin KaFai Lau 	dst->state = src->state;
836f521a2bSMartin KaFai Lau }
846f521a2bSMartin KaFai Lau 
tpcpy(struct bpf_tcp_sock * dst,const struct bpf_tcp_sock * src)856f521a2bSMartin KaFai Lau static void tpcpy(struct bpf_tcp_sock *dst,
866f521a2bSMartin KaFai Lau 		  const struct bpf_tcp_sock *src)
876f521a2bSMartin KaFai Lau {
886f521a2bSMartin KaFai Lau 	dst->snd_cwnd = src->snd_cwnd;
896f521a2bSMartin KaFai Lau 	dst->srtt_us = src->srtt_us;
906f521a2bSMartin KaFai Lau 	dst->rtt_min = src->rtt_min;
916f521a2bSMartin KaFai Lau 	dst->snd_ssthresh = src->snd_ssthresh;
926f521a2bSMartin KaFai Lau 	dst->rcv_nxt = src->rcv_nxt;
936f521a2bSMartin KaFai Lau 	dst->snd_nxt = src->snd_nxt;
946f521a2bSMartin KaFai Lau 	dst->snd_una = src->snd_una;
956f521a2bSMartin KaFai Lau 	dst->mss_cache = src->mss_cache;
966f521a2bSMartin KaFai Lau 	dst->ecn_flags = src->ecn_flags;
976f521a2bSMartin KaFai Lau 	dst->rate_delivered = src->rate_delivered;
986f521a2bSMartin KaFai Lau 	dst->rate_interval_us = src->rate_interval_us;
996f521a2bSMartin KaFai Lau 	dst->packets_out = src->packets_out;
1006f521a2bSMartin KaFai Lau 	dst->retrans_out = src->retrans_out;
1016f521a2bSMartin KaFai Lau 	dst->total_retrans = src->total_retrans;
1026f521a2bSMartin KaFai Lau 	dst->segs_in = src->segs_in;
1036f521a2bSMartin KaFai Lau 	dst->data_segs_in = src->data_segs_in;
1046f521a2bSMartin KaFai Lau 	dst->segs_out = src->segs_out;
1056f521a2bSMartin KaFai Lau 	dst->data_segs_out = src->data_segs_out;
1066f521a2bSMartin KaFai Lau 	dst->lost_out = src->lost_out;
1076f521a2bSMartin KaFai Lau 	dst->sacked_out = src->sacked_out;
1086f521a2bSMartin KaFai Lau 	dst->bytes_received = src->bytes_received;
1096f521a2bSMartin KaFai Lau 	dst->bytes_acked = src->bytes_acked;
1106f521a2bSMartin KaFai Lau }
1116f521a2bSMartin KaFai Lau 
112b18c1f0aSMartin KaFai Lau /* Always return CG_OK so that no pkt will be filtered out */
113b18c1f0aSMartin KaFai Lau #define CG_OK 1
114b18c1f0aSMartin KaFai Lau 
115b18c1f0aSMartin KaFai Lau #define RET_LOG() ({						\
1166f521a2bSMartin KaFai Lau 	linum = __LINE__;					\
117a4c9fe0eSJakub Sitnicki 	bpf_map_update_elem(&linum_map, &linum_idx, &linum, BPF_ANY);	\
118b18c1f0aSMartin KaFai Lau 	return CG_OK;						\
119b18c1f0aSMartin KaFai Lau })
1206f521a2bSMartin KaFai Lau 
1216f521a2bSMartin KaFai Lau SEC("cgroup_skb/egress")
egress_read_sock_fields(struct __sk_buff * skb)1226f521a2bSMartin KaFai Lau int egress_read_sock_fields(struct __sk_buff *skb)
1236f521a2bSMartin KaFai Lau {
124c8ed6685SAndrii Nakryiko 	struct bpf_spinlock_cnt cli_cnt_init = { .lock = {}, .cnt = 0xeB9F };
1256f521a2bSMartin KaFai Lau 	struct bpf_spinlock_cnt *pkt_out_cnt, *pkt_out_cnt10;
1266f521a2bSMartin KaFai Lau 	struct bpf_tcp_sock *tp, *tp_ret;
1276f521a2bSMartin KaFai Lau 	struct bpf_sock *sk, *sk_ret;
1286f521a2bSMartin KaFai Lau 	__u32 linum, linum_idx;
129edc2d66aSMartin KaFai Lau 	struct tcp_sock *ktp;
1306f521a2bSMartin KaFai Lau 
1316f521a2bSMartin KaFai Lau 	linum_idx = EGRESS_LINUM_IDX;
1326f521a2bSMartin KaFai Lau 
1336f521a2bSMartin KaFai Lau 	sk = skb->sk;
134b18c1f0aSMartin KaFai Lau 	if (!sk)
135b18c1f0aSMartin KaFai Lau 		RET_LOG();
1366f521a2bSMartin KaFai Lau 
137e06b5bbcSJakub Sitnicki 	/* Not testing the egress traffic or the listening socket,
138e06b5bbcSJakub Sitnicki 	 * which are covered by the cgroup_skb/ingress test program.
139b18c1f0aSMartin KaFai Lau 	 */
140b18c1f0aSMartin KaFai Lau 	if (sk->family != AF_INET6 || !is_loopback6(sk->src_ip6) ||
141e06b5bbcSJakub Sitnicki 	    sk->state == BPF_TCP_LISTEN)
142b18c1f0aSMartin KaFai Lau 		return CG_OK;
143b18c1f0aSMartin KaFai Lau 
144b18c1f0aSMartin KaFai Lau 	if (sk->src_port == bpf_ntohs(srv_sa6.sin6_port)) {
145b18c1f0aSMartin KaFai Lau 		/* Server socket */
146b18c1f0aSMartin KaFai Lau 		sk_ret = &srv_sk;
147b18c1f0aSMartin KaFai Lau 		tp_ret = &srv_tp;
148b18c1f0aSMartin KaFai Lau 	} else if (sk->dst_port == srv_sa6.sin6_port) {
149b18c1f0aSMartin KaFai Lau 		/* Client socket */
150b18c1f0aSMartin KaFai Lau 		sk_ret = &cli_sk;
151b18c1f0aSMartin KaFai Lau 		tp_ret = &cli_tp;
152b18c1f0aSMartin KaFai Lau 	} else {
153b18c1f0aSMartin KaFai Lau 		/* Not the testing egress traffic */
154b18c1f0aSMartin KaFai Lau 		return CG_OK;
155b18c1f0aSMartin KaFai Lau 	}
156b18c1f0aSMartin KaFai Lau 
157b18c1f0aSMartin KaFai Lau 	/* It must be a fullsock for cgroup_skb/egress prog */
1586f521a2bSMartin KaFai Lau 	sk = bpf_sk_fullsock(sk);
159b18c1f0aSMartin KaFai Lau 	if (!sk)
160b18c1f0aSMartin KaFai Lau 		RET_LOG();
161b18c1f0aSMartin KaFai Lau 
162b18c1f0aSMartin KaFai Lau 	/* Not the testing egress traffic */
163b18c1f0aSMartin KaFai Lau 	if (sk->protocol != IPPROTO_TCP)
164b18c1f0aSMartin KaFai Lau 		return CG_OK;
1656f521a2bSMartin KaFai Lau 
1666f521a2bSMartin KaFai Lau 	tp = bpf_tcp_sock(sk);
1676f521a2bSMartin KaFai Lau 	if (!tp)
168b18c1f0aSMartin KaFai Lau 		RET_LOG();
1696f521a2bSMartin KaFai Lau 
1706f521a2bSMartin KaFai Lau 	skcpy(sk_ret, sk);
1716f521a2bSMartin KaFai Lau 	tpcpy(tp_ret, tp);
1726f521a2bSMartin KaFai Lau 
173b18c1f0aSMartin KaFai Lau 	if (sk_ret == &srv_sk) {
174edc2d66aSMartin KaFai Lau 		ktp = bpf_skc_to_tcp_sock(sk);
175edc2d66aSMartin KaFai Lau 
176edc2d66aSMartin KaFai Lau 		if (!ktp)
177edc2d66aSMartin KaFai Lau 			RET_LOG();
178edc2d66aSMartin KaFai Lau 
179edc2d66aSMartin KaFai Lau 		lsndtime = ktp->lsndtime;
180edc2d66aSMartin KaFai Lau 
181edc2d66aSMartin KaFai Lau 		child_cg_id = bpf_sk_cgroup_id(ktp);
182edc2d66aSMartin KaFai Lau 		if (!child_cg_id)
183edc2d66aSMartin KaFai Lau 			RET_LOG();
184edc2d66aSMartin KaFai Lau 
185edc2d66aSMartin KaFai Lau 		parent_cg_id = bpf_sk_ancestor_cgroup_id(ktp, 2);
186edc2d66aSMartin KaFai Lau 		if (!parent_cg_id)
187edc2d66aSMartin KaFai Lau 			RET_LOG();
188edc2d66aSMartin KaFai Lau 
1896f521a2bSMartin KaFai Lau 		/* The userspace has created it for srv sk */
190edc2d66aSMartin KaFai Lau 		pkt_out_cnt = bpf_sk_storage_get(&sk_pkt_out_cnt, ktp, 0, 0);
191edc2d66aSMartin KaFai Lau 		pkt_out_cnt10 = bpf_sk_storage_get(&sk_pkt_out_cnt10, ktp,
1926f521a2bSMartin KaFai Lau 						   0, 0);
1936f521a2bSMartin KaFai Lau 	} else {
1946f521a2bSMartin KaFai Lau 		pkt_out_cnt = bpf_sk_storage_get(&sk_pkt_out_cnt, sk,
1956f521a2bSMartin KaFai Lau 						 &cli_cnt_init,
1966f521a2bSMartin KaFai Lau 						 BPF_SK_STORAGE_GET_F_CREATE);
1976f521a2bSMartin KaFai Lau 		pkt_out_cnt10 = bpf_sk_storage_get(&sk_pkt_out_cnt10,
1986f521a2bSMartin KaFai Lau 						   sk, &cli_cnt_init,
1996f521a2bSMartin KaFai Lau 						   BPF_SK_STORAGE_GET_F_CREATE);
2006f521a2bSMartin KaFai Lau 	}
2016f521a2bSMartin KaFai Lau 
2026f521a2bSMartin KaFai Lau 	if (!pkt_out_cnt || !pkt_out_cnt10)
203b18c1f0aSMartin KaFai Lau 		RET_LOG();
2046f521a2bSMartin KaFai Lau 
2056f521a2bSMartin KaFai Lau 	/* Even both cnt and cnt10 have lock defined in their BTF,
2066f521a2bSMartin KaFai Lau 	 * intentionally one cnt takes lock while one does not
2076f521a2bSMartin KaFai Lau 	 * as a test for the spinlock support in BPF_MAP_TYPE_SK_STORAGE.
2086f521a2bSMartin KaFai Lau 	 */
2096f521a2bSMartin KaFai Lau 	pkt_out_cnt->cnt += 1;
2106f521a2bSMartin KaFai Lau 	bpf_spin_lock(&pkt_out_cnt10->lock);
2116f521a2bSMartin KaFai Lau 	pkt_out_cnt10->cnt += 10;
2126f521a2bSMartin KaFai Lau 	bpf_spin_unlock(&pkt_out_cnt10->lock);
2136f521a2bSMartin KaFai Lau 
214b18c1f0aSMartin KaFai Lau 	return CG_OK;
2156f521a2bSMartin KaFai Lau }
2166f521a2bSMartin KaFai Lau 
2176f521a2bSMartin KaFai Lau SEC("cgroup_skb/ingress")
ingress_read_sock_fields(struct __sk_buff * skb)2186f521a2bSMartin KaFai Lau int ingress_read_sock_fields(struct __sk_buff *skb)
2196f521a2bSMartin KaFai Lau {
220b18c1f0aSMartin KaFai Lau 	struct bpf_tcp_sock *tp;
2216f521a2bSMartin KaFai Lau 	__u32 linum, linum_idx;
222b18c1f0aSMartin KaFai Lau 	struct bpf_sock *sk;
2236f521a2bSMartin KaFai Lau 
2246f521a2bSMartin KaFai Lau 	linum_idx = INGRESS_LINUM_IDX;
2256f521a2bSMartin KaFai Lau 
2266f521a2bSMartin KaFai Lau 	sk = skb->sk;
2276f521a2bSMartin KaFai Lau 	if (!sk)
228b18c1f0aSMartin KaFai Lau 		RET_LOG();
229b18c1f0aSMartin KaFai Lau 
230b18c1f0aSMartin KaFai Lau 	/* Not the testing ingress traffic to the server */
231b18c1f0aSMartin KaFai Lau 	if (sk->family != AF_INET6 || !is_loopback6(sk->src_ip6) ||
232b18c1f0aSMartin KaFai Lau 	    sk->src_port != bpf_ntohs(srv_sa6.sin6_port))
233b18c1f0aSMartin KaFai Lau 		return CG_OK;
234b18c1f0aSMartin KaFai Lau 
235e06b5bbcSJakub Sitnicki 	/* Only interested in the listening socket */
236e06b5bbcSJakub Sitnicki 	if (sk->state != BPF_TCP_LISTEN)
237b18c1f0aSMartin KaFai Lau 		return CG_OK;
238b18c1f0aSMartin KaFai Lau 
239b18c1f0aSMartin KaFai Lau 	/* It must be a fullsock for cgroup_skb/ingress prog */
240b18c1f0aSMartin KaFai Lau 	sk = bpf_sk_fullsock(sk);
241b18c1f0aSMartin KaFai Lau 	if (!sk)
242b18c1f0aSMartin KaFai Lau 		RET_LOG();
2436f521a2bSMartin KaFai Lau 
2446f521a2bSMartin KaFai Lau 	tp = bpf_tcp_sock(sk);
2456f521a2bSMartin KaFai Lau 	if (!tp)
246b18c1f0aSMartin KaFai Lau 		RET_LOG();
2476f521a2bSMartin KaFai Lau 
248b18c1f0aSMartin KaFai Lau 	skcpy(&listen_sk, sk);
249b18c1f0aSMartin KaFai Lau 	tpcpy(&listen_tp, tp);
2506f521a2bSMartin KaFai Lau 
251b18c1f0aSMartin KaFai Lau 	return CG_OK;
2526f521a2bSMartin KaFai Lau }
2536f521a2bSMartin KaFai Lau 
254deb59400SJakub Sitnicki /*
255deb59400SJakub Sitnicki  * NOTE: 4-byte load from bpf_sock at dst_port offset is quirky. It
256deb59400SJakub Sitnicki  * gets rewritten by the access converter to a 2-byte load for
257deb59400SJakub Sitnicki  * backward compatibility. Treating the load result as a be16 value
258deb59400SJakub Sitnicki  * makes the code portable across little- and big-endian platforms.
259deb59400SJakub Sitnicki  */
sk_dst_port__load_word(struct bpf_sock * sk)2608f50f16fSJakub Sitnicki static __noinline bool sk_dst_port__load_word(struct bpf_sock *sk)
2618f50f16fSJakub Sitnicki {
2628f50f16fSJakub Sitnicki 	__u32 *word = (__u32 *)&sk->dst_port;
263deb59400SJakub Sitnicki 	return word[0] == bpf_htons(0xcafe);
2648f50f16fSJakub Sitnicki }
2658f50f16fSJakub Sitnicki 
sk_dst_port__load_half(struct bpf_sock * sk)2668f50f16fSJakub Sitnicki static __noinline bool sk_dst_port__load_half(struct bpf_sock *sk)
2678f50f16fSJakub Sitnicki {
268*de58ef41SYonghong Song 	__u16 *half;
269*de58ef41SYonghong Song 
270*de58ef41SYonghong Song 	asm volatile ("");
271*de58ef41SYonghong Song 	half = (__u16 *)&sk->dst_port;
2728f50f16fSJakub Sitnicki 	return half[0] == bpf_htons(0xcafe);
2738f50f16fSJakub Sitnicki }
2748f50f16fSJakub Sitnicki 
sk_dst_port__load_byte(struct bpf_sock * sk)2758f50f16fSJakub Sitnicki static __noinline bool sk_dst_port__load_byte(struct bpf_sock *sk)
2768f50f16fSJakub Sitnicki {
2778f50f16fSJakub Sitnicki 	__u8 *byte = (__u8 *)&sk->dst_port;
2788f50f16fSJakub Sitnicki 	return byte[0] == 0xca && byte[1] == 0xfe;
2798f50f16fSJakub Sitnicki }
2808f50f16fSJakub Sitnicki 
2818f50f16fSJakub Sitnicki SEC("cgroup_skb/egress")
read_sk_dst_port(struct __sk_buff * skb)2828f50f16fSJakub Sitnicki int read_sk_dst_port(struct __sk_buff *skb)
2838f50f16fSJakub Sitnicki {
2848f50f16fSJakub Sitnicki 	__u32 linum, linum_idx;
2858f50f16fSJakub Sitnicki 	struct bpf_sock *sk;
2868f50f16fSJakub Sitnicki 
2878f50f16fSJakub Sitnicki 	linum_idx = READ_SK_DST_PORT_LINUM_IDX;
2888f50f16fSJakub Sitnicki 
2898f50f16fSJakub Sitnicki 	sk = skb->sk;
2908f50f16fSJakub Sitnicki 	if (!sk)
2918f50f16fSJakub Sitnicki 		RET_LOG();
2928f50f16fSJakub Sitnicki 
2932d2202baSJakub Sitnicki 	/* Ignore everything but the SYN from the client socket */
2942d2202baSJakub Sitnicki 	if (sk->state != BPF_TCP_SYN_SENT)
2952d2202baSJakub Sitnicki 		return CG_OK;
2962d2202baSJakub Sitnicki 
2978f50f16fSJakub Sitnicki 	if (!sk_dst_port__load_word(sk))
2988f50f16fSJakub Sitnicki 		RET_LOG();
2998f50f16fSJakub Sitnicki 	if (!sk_dst_port__load_half(sk))
3008f50f16fSJakub Sitnicki 		RET_LOG();
3018f50f16fSJakub Sitnicki 	if (!sk_dst_port__load_byte(sk))
3028f50f16fSJakub Sitnicki 		RET_LOG();
3038f50f16fSJakub Sitnicki 
3048f50f16fSJakub Sitnicki 	return CG_OK;
3058f50f16fSJakub Sitnicki }
3068f50f16fSJakub Sitnicki 
3076f521a2bSMartin KaFai Lau char _license[] SEC("license") = "GPL";
308