18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2018 Facebook
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * BPF program to automatically reflect TOS option from received syn packet
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Use "bpftool cgroup attach $cg sock_ops $prog" to load this BPF program.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <uapi/linux/bpf.h>
118c2ecf20Sopenharmony_ci#include <uapi/linux/tcp.h>
128c2ecf20Sopenharmony_ci#include <uapi/linux/if_ether.h>
138c2ecf20Sopenharmony_ci#include <uapi/linux/if_packet.h>
148c2ecf20Sopenharmony_ci#include <uapi/linux/ip.h>
158c2ecf20Sopenharmony_ci#include <uapi/linux/ipv6.h>
168c2ecf20Sopenharmony_ci#include <uapi/linux/in.h>
178c2ecf20Sopenharmony_ci#include <linux/socket.h>
188c2ecf20Sopenharmony_ci#include <bpf/bpf_helpers.h>
198c2ecf20Sopenharmony_ci#include <bpf/bpf_endian.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define DEBUG 1
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ciSEC("sockops")
248c2ecf20Sopenharmony_ciint bpf_basertt(struct bpf_sock_ops *skops)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	char header[sizeof(struct ipv6hdr)];
278c2ecf20Sopenharmony_ci	struct ipv6hdr *hdr6;
288c2ecf20Sopenharmony_ci	struct iphdr *hdr;
298c2ecf20Sopenharmony_ci	int hdr_size = 0;
308c2ecf20Sopenharmony_ci	int save_syn = 1;
318c2ecf20Sopenharmony_ci	int tos = 0;
328c2ecf20Sopenharmony_ci	int rv = 0;
338c2ecf20Sopenharmony_ci	int op;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	op = (int) skops->op;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#ifdef DEBUG
388c2ecf20Sopenharmony_ci	bpf_printk("BPF command: %d\n", op);
398c2ecf20Sopenharmony_ci#endif
408c2ecf20Sopenharmony_ci	switch (op) {
418c2ecf20Sopenharmony_ci	case BPF_SOCK_OPS_TCP_LISTEN_CB:
428c2ecf20Sopenharmony_ci		rv = bpf_setsockopt(skops, SOL_TCP, TCP_SAVE_SYN,
438c2ecf20Sopenharmony_ci				   &save_syn, sizeof(save_syn));
448c2ecf20Sopenharmony_ci		break;
458c2ecf20Sopenharmony_ci	case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
468c2ecf20Sopenharmony_ci		if (skops->family == AF_INET)
478c2ecf20Sopenharmony_ci			hdr_size = sizeof(struct iphdr);
488c2ecf20Sopenharmony_ci		else
498c2ecf20Sopenharmony_ci			hdr_size = sizeof(struct ipv6hdr);
508c2ecf20Sopenharmony_ci		rv = bpf_getsockopt(skops, SOL_TCP, TCP_SAVED_SYN,
518c2ecf20Sopenharmony_ci				    header, hdr_size);
528c2ecf20Sopenharmony_ci		if (!rv) {
538c2ecf20Sopenharmony_ci			if (skops->family == AF_INET) {
548c2ecf20Sopenharmony_ci				hdr = (struct iphdr *) header;
558c2ecf20Sopenharmony_ci				tos = hdr->tos;
568c2ecf20Sopenharmony_ci				if (tos != 0)
578c2ecf20Sopenharmony_ci					bpf_setsockopt(skops, SOL_IP, IP_TOS,
588c2ecf20Sopenharmony_ci						       &tos, sizeof(tos));
598c2ecf20Sopenharmony_ci			} else {
608c2ecf20Sopenharmony_ci				hdr6 = (struct ipv6hdr *) header;
618c2ecf20Sopenharmony_ci				tos = ((hdr6->priority) << 4 |
628c2ecf20Sopenharmony_ci				       (hdr6->flow_lbl[0]) >>  4);
638c2ecf20Sopenharmony_ci				if (tos)
648c2ecf20Sopenharmony_ci					bpf_setsockopt(skops, SOL_IPV6,
658c2ecf20Sopenharmony_ci						       IPV6_TCLASS,
668c2ecf20Sopenharmony_ci						       &tos, sizeof(tos));
678c2ecf20Sopenharmony_ci			}
688c2ecf20Sopenharmony_ci			rv = 0;
698c2ecf20Sopenharmony_ci		}
708c2ecf20Sopenharmony_ci		break;
718c2ecf20Sopenharmony_ci	default:
728c2ecf20Sopenharmony_ci		rv = -1;
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci#ifdef DEBUG
758c2ecf20Sopenharmony_ci	bpf_printk("Returning %d\n", rv);
768c2ecf20Sopenharmony_ci#endif
778c2ecf20Sopenharmony_ci	skops->reply = rv;
788c2ecf20Sopenharmony_ci	return 1;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_cichar _license[] SEC("license") = "GPL";
81