17568f4cbSAndrey Ignatov // SPDX-License-Identifier: GPL-2.0
27568f4cbSAndrey Ignatov // Copyright (c) 2019 Facebook
37568f4cbSAndrey Ignatov 
47568f4cbSAndrey Ignatov #include <stdint.h>
57568f4cbSAndrey Ignatov #include <string.h>
67568f4cbSAndrey Ignatov 
77568f4cbSAndrey Ignatov #include <linux/stddef.h>
87568f4cbSAndrey Ignatov #include <linux/bpf.h>
97568f4cbSAndrey Ignatov 
103e689141SToke Høiland-Jørgensen #include <bpf/bpf_helpers.h>
117568f4cbSAndrey Ignatov 
127568f4cbSAndrey Ignatov /* Max supported length of a string with unsigned long in base 10 (pow2 - 1). */
137568f4cbSAndrey Ignatov #define MAX_ULONG_STR_LEN 0xF
147568f4cbSAndrey Ignatov 
157568f4cbSAndrey Ignatov /* Max supported length of sysctl value string (pow2). */
167568f4cbSAndrey Ignatov #define MAX_VALUE_STR_LEN 0x40
177568f4cbSAndrey Ignatov 
18ebb88607SHechao Li #ifndef ARRAY_SIZE
19ebb88607SHechao Li #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
20ebb88607SHechao Li #endif
21ebb88607SHechao Li 
226e057fc1SYonghong Song const char tcp_mem_name[] = "net/ipv4/tcp_mem";
is_tcp_mem(struct bpf_sysctl * ctx)237568f4cbSAndrey Ignatov static __always_inline int is_tcp_mem(struct bpf_sysctl *ctx)
247568f4cbSAndrey Ignatov {
257568f4cbSAndrey Ignatov 	unsigned char i;
266e057fc1SYonghong Song 	char name[sizeof(tcp_mem_name)];
277568f4cbSAndrey Ignatov 	int ret;
287568f4cbSAndrey Ignatov 
297568f4cbSAndrey Ignatov 	memset(name, 0, sizeof(name));
307568f4cbSAndrey Ignatov 	ret = bpf_sysctl_get_name(ctx, name, sizeof(name), 0);
317568f4cbSAndrey Ignatov 	if (ret < 0 || ret != sizeof(tcp_mem_name) - 1)
327568f4cbSAndrey Ignatov 		return 0;
337568f4cbSAndrey Ignatov 
347568f4cbSAndrey Ignatov #pragma clang loop unroll(full)
357568f4cbSAndrey Ignatov 	for (i = 0; i < sizeof(tcp_mem_name); ++i)
367568f4cbSAndrey Ignatov 		if (name[i] != tcp_mem_name[i])
377568f4cbSAndrey Ignatov 			return 0;
387568f4cbSAndrey Ignatov 
397568f4cbSAndrey Ignatov 	return 1;
407568f4cbSAndrey Ignatov }
417568f4cbSAndrey Ignatov 
427568f4cbSAndrey Ignatov SEC("cgroup/sysctl")
sysctl_tcp_mem(struct bpf_sysctl * ctx)437568f4cbSAndrey Ignatov int sysctl_tcp_mem(struct bpf_sysctl *ctx)
447568f4cbSAndrey Ignatov {
457568f4cbSAndrey Ignatov 	unsigned long tcp_mem[3] = {0, 0, 0};
467568f4cbSAndrey Ignatov 	char value[MAX_VALUE_STR_LEN];
477568f4cbSAndrey Ignatov 	unsigned char i, off = 0;
489b329d0dSAlexei Starovoitov 	volatile int ret;
497568f4cbSAndrey Ignatov 
507568f4cbSAndrey Ignatov 	if (ctx->write)
517568f4cbSAndrey Ignatov 		return 0;
527568f4cbSAndrey Ignatov 
537568f4cbSAndrey Ignatov 	if (!is_tcp_mem(ctx))
547568f4cbSAndrey Ignatov 		return 0;
557568f4cbSAndrey Ignatov 
567568f4cbSAndrey Ignatov 	ret = bpf_sysctl_get_current_value(ctx, value, MAX_VALUE_STR_LEN);
577568f4cbSAndrey Ignatov 	if (ret < 0 || ret >= MAX_VALUE_STR_LEN)
587568f4cbSAndrey Ignatov 		return 0;
597568f4cbSAndrey Ignatov 
607568f4cbSAndrey Ignatov #pragma clang loop unroll(full)
617568f4cbSAndrey Ignatov 	for (i = 0; i < ARRAY_SIZE(tcp_mem); ++i) {
627568f4cbSAndrey Ignatov 		ret = bpf_strtoul(value + off, MAX_ULONG_STR_LEN, 0,
637568f4cbSAndrey Ignatov 				  tcp_mem + i);
647568f4cbSAndrey Ignatov 		if (ret <= 0 || ret > MAX_ULONG_STR_LEN)
657568f4cbSAndrey Ignatov 			return 0;
667568f4cbSAndrey Ignatov 		off += ret & MAX_ULONG_STR_LEN;
677568f4cbSAndrey Ignatov 	}
687568f4cbSAndrey Ignatov 
697568f4cbSAndrey Ignatov 
707568f4cbSAndrey Ignatov 	return tcp_mem[0] < tcp_mem[1] && tcp_mem[1] < tcp_mem[2];
717568f4cbSAndrey Ignatov }
727568f4cbSAndrey Ignatov 
737568f4cbSAndrey Ignatov char _license[] SEC("license") = "GPL";
74