-
Notifications
You must be signed in to change notification settings - Fork 12
/
build.rs
183 lines (161 loc) · 5.61 KB
/
build.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// SPDX-License-Identifier: GPL-2.0-or-later
//
// BPFContain - Container security with eBPF
// Copyright (C) 2020 William Findlay
//
// Dec. 29, 2020 William Findlay Created this.
use std::{
env,
fs::{remove_file, File},
io::{BufWriter, Write},
os::unix::{fs::symlink, prelude::MetadataExt},
path::{Path, PathBuf},
process::{Command, Stdio},
};
use libbpf_cargo::SkeletonBuilder;
use tempfile::tempdir;
use uname::uname;
fn main() {
// Re-run build if bpfcontain.bpf.c has changed
println!("cargo:rerun-if-changed=src/bpf/bpfcontain.bpf.c");
// Re-run build if our header files have changed
println!("cargo:rerun-if-changed=bindings.h");
for path in glob::glob("src/bpf/include/*.h")
.expect("Failed to glob headers")
.filter_map(Result::ok)
{
println!("cargo:rerun-if-changed={}", path.display());
}
generate_vmlinux();
generate_bindings();
generate_skeleton();
}
fn bindgen_builder() -> bindgen::Builder {
bindgen::builder()
.derive_default(true)
.derive_eq(true)
.derive_partialeq(true)
.default_enum_style(bindgen::EnumVariation::Rust {
non_exhaustive: false,
})
.clang_arg("-Isrc/bpf/include")
.clang_arg("-Wno-unknown-attributes")
.clang_arg("-target")
.clang_arg("bpf")
.ignore_functions()
}
fn generate_bindings() {
let bindings_out = PathBuf::from(format!("{}/bindings.rs", env::var("OUT_DIR").unwrap()));
let vmlinux_bindings_out = PathBuf::from(format!(
"{}/vmlinux_bindings.rs",
env::var("OUT_DIR").unwrap()
));
// Generate bpfcontain api bindings
let bindings = bindgen_builder()
.header("bindings.h")
.blocklist_file(".*/vmlinux.h.*")
.constified_enum_module("policy_decision_t")
.constified_enum_module("file_permission_t")
.constified_enum_module("capability_t")
.constified_enum_module("net_operation_t")
.constified_enum_module("signal_operation_t")
.bitfield_enum("audit_level_t")
.generate()
.expect("Failed to generate bpfcontain api bindings");
// Generate int bindings from vmlinux
let vmlinux_bindings = bindgen_builder()
.header("bindings.h")
.allowlist_type("s8")
.allowlist_type("s16")
.allowlist_type("s32")
.allowlist_type("s64")
.allowlist_type("u8_")
.allowlist_type("u16_")
.allowlist_type("u32_")
.allowlist_type("u64_")
.allowlist_type("bool")
.generate()
.expect("Failed to generate vmlinux type bindings");
// Save bindings
bindings
.write_to_file(bindings_out)
.expect("Failed to save bindings");
vmlinux_bindings
.write_to_file(vmlinux_bindings_out)
.expect("Failed to save bindings");
}
fn generate_skeleton() {
let dir = tempdir()
.expect("Failed to create temporary directory")
.into_path();
let mut builder = SkeletonBuilder::new();
builder
.source("src/bpf/bpfcontain.bpf.c")
.clang_args("-O2 -Isrc/bpf/include -Wno-unknown-attributes -emit-llvm")
.obj(dir.join("bpfcontain.bpf.bc"))
.build()
.expect("Failed to build");
Command::new("llc")
.args(
format!(
"{} -mattr=+alu32 -march=bpf -mcpu=v2 -filetype=obj -o {}",
dir.join("bpfcontain.bpf.bc").display(),
dir.join("bpfcontain.bpf.o").display()
)
.split_whitespace(),
)
.status()
.expect("Failed to run llc");
builder
.obj(dir.join("bpfcontain.bpf.o"))
.generate("src/bpf/mod.rs")
.expect("Failed to generate skeleton")
}
/// Checks if a file exists and is non-empty
fn nonempty_exists(path: &Path) -> bool {
path.exists()
&& File::open(path)
.and_then(|open_file| open_file.metadata())
.map(|metadata| metadata.size() != 0)
.unwrap_or(false)
}
fn generate_vmlinux() {
// Determine pathname for vmlinux header
let kernel_release = uname().expect("Failed to fetch system information").release;
let vmlinux_path = PathBuf::from(format!("src/bpf/include/vmlinux_{}.h", kernel_release));
let vmlinux_link_path = PathBuf::from("src/bpf/include/vmlinux.h");
// Populate vmlinux_{kernel_release}.h with BTF info
if !nonempty_exists(&vmlinux_path) {
let mut vmlinux_writer = BufWriter::new(
File::create(vmlinux_path.clone())
.expect("Failed to open vmlinux destination for writing"),
);
let output = Command::new("bpftool")
.arg("btf")
.arg("dump")
.arg("file")
.arg("/sys/kernel/btf/vmlinux")
.arg("format")
.arg("c")
.stdout(Stdio::piped())
.output()
.expect("Failed to run bpftool");
// Make sure we were able to run bpftool successfully
assert!(output.status.success(), "Failed to get BTF info");
// Make sure we actually have something to write to vmlinux.h
assert!(
!output.stdout.is_empty(),
"bpftool codegen output was empty"
);
vmlinux_writer
.write_all(&output.stdout)
.expect("Failed to write to vmlinux.h");
}
// Remove existing link if it exists
if vmlinux_link_path.exists() {
remove_file(vmlinux_link_path.clone()).expect("Failed to unlink vmlinux.h");
}
// Create a new symlink
symlink(vmlinux_path.file_name().unwrap(), vmlinux_link_path)
.expect("Failed to symlink vmlinux.h");
}