main.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811
  1. #include <iostream>
  2. #include <fstream>
  3. #include <csignal>
  4. #include <sstream>
  5. #include <utility>
  6. #include <vector>
  7. #include <unordered_set>
  8. #include <regex>
  9. #include <phpcpp.h>
  10. #include <BPF.h>
  11. #include <string>
  12. #include "main.h"
  13. #include "libbpf.h"
  14. #include "bpf_module.h"
  15. #include "bcc_common.h"
  16. #include "table.h"
  17. #include "function.h"
  18. #define TRACE_PIPE_PATH "/sys/kernel/debug/tracing/trace_pipe"
  19. #define DEBUGFS "/sys/kernel/debug"
  20. const std::vector<std::string> syscall_prefixes = {
  21. "sys_",
  22. "__x64_sys_",
  23. "__x32_compat_sys_",
  24. "__ia32_compat_sys_",
  25. "__arm64_sys_",
  26. "__s390x_sys_",
  27. "__s390_sys_",
  28. "__riscv_sys_"
  29. };
  30. class EbpfExtension : public Php::Base {
  31. private:
  32. ebpf::BPF bpf;
  33. void *mod;
  34. Php::Object _perf_event;
  35. Php::Value _class_perf_event_obj;
  36. static std::string add_prefix(const std::string &prefix, const std::string &name) {
  37. if (name.rfind(prefix, 0) != 0) {
  38. return prefix + name;
  39. }
  40. return name;
  41. }
  42. std::string fix_syscall_fnname(const std::string &name) {
  43. for (const auto &prefix: syscall_prefixes) {
  44. if (name.rfind(prefix, 0) == 0) {
  45. return bpf.get_syscall_fnname(name.substr(prefix.length()));
  46. }
  47. }
  48. return name;
  49. }
  50. void _trace_autoload() {
  51. size_t num_funcs = bpf.get_num_functions();
  52. for (size_t i = 0; i < num_funcs; i++) {
  53. const char *func_name = bpf.get_function_name(i);
  54. std::string fn_name(func_name);
  55. if (fn_name.rfind("kprobe__", 0) == 0) {
  56. std::string kernel_func = fix_syscall_fnname(fn_name.substr(8));
  57. bpf.attach_kprobe(kernel_func, fn_name);
  58. } else if (fn_name.rfind("kretprobe__", 0) == 0) {
  59. std::string kernel_func = fix_syscall_fnname(fn_name.substr(11));
  60. bpf.attach_kprobe(kernel_func, fn_name, 0, BPF_PROBE_RETURN);
  61. } else if (fn_name.rfind("tracepoint__", 0) == 0) {
  62. std::string tp_name = fn_name.substr(12);
  63. size_t sep = tp_name.find("__");
  64. if (sep != std::string::npos) {
  65. tp_name.replace(sep, 2, ":");
  66. }
  67. bpf.attach_tracepoint(tp_name, fn_name);
  68. } else if (fn_name.rfind("raw_tracepoint__", 0) == 0) {
  69. std::string tp_name = fn_name.substr(16);
  70. bpf.attach_raw_tracepoint(tp_name, fn_name);
  71. } else if (fn_name.rfind("kfunc__", 0) == 0) {
  72. fn_name = add_prefix("kfunc__", fn_name);
  73. attach_kfunc(fn_name);
  74. } else if (fn_name.rfind("kretfunc__", 0) == 0) {
  75. fn_name = add_prefix("kretfunc__", fn_name);
  76. attach_kfunc(fn_name);
  77. } else if (fn_name.rfind("lsm__", 0) == 0) {
  78. fn_name = add_prefix("lsm__", fn_name);
  79. attach_lsm(fn_name);
  80. }
  81. }
  82. }
  83. public:
  84. EbpfExtension() = default;
  85. virtual ~EbpfExtension() = default;
  86. Php::Value __get(const Php::Value &name) {
  87. int from_attr = 1;
  88. auto res = get_table_cls(name, from_attr);
  89. if (!res.isObject()) {
  90. return Php::Base::__get(name);
  91. } else {
  92. return res;
  93. }
  94. }
  95. std::unordered_set<std::string> get_kprobe_functions(const std::string &event_re) {
  96. std::unordered_set<std::string> blacklist;
  97. std::unordered_set<std::string> avail_filter;
  98. std::unordered_set<std::string> fns;
  99. std::string blacklist_file = std::string(DEBUGFS) + "/kprobes/blacklist";
  100. std::ifstream blacklist_f(blacklist_file);
  101. if (blacklist_f.is_open()) {
  102. std::string line;
  103. while (std::getline(blacklist_f, line)) {
  104. std::istringstream iss(line);
  105. std::string addr, func_name;
  106. if (iss >> addr >> func_name) {
  107. blacklist.insert(func_name);
  108. }
  109. }
  110. blacklist_f.close();
  111. }
  112. std::string avail_filter_file = std::string(DEBUGFS) + "/tracing/available_filter_functions";
  113. std::ifstream avail_filter_f(avail_filter_file);
  114. if (avail_filter_f.is_open()) {
  115. std::string line;
  116. while (std::getline(avail_filter_f, line)) {
  117. std::istringstream iss(line);
  118. std::string func_name;
  119. if (iss >> func_name) {
  120. avail_filter.insert(func_name);
  121. }
  122. }
  123. avail_filter_f.close();
  124. }
  125. std::ifstream kallsyms_f("/proc/kallsyms");
  126. if (!kallsyms_f.is_open()) {
  127. std::cerr << "Failed to open /proc/kallsyms\n";
  128. return fns;
  129. }
  130. std::string line;
  131. bool in_init_section = false;
  132. bool in_irq_section = false;
  133. std::regex cold_regex(".*\\.cold(\\.\\d+)?$");
  134. while (std::getline(kallsyms_f, line)) {
  135. std::istringstream iss(line);
  136. std::string addr, type, func_name;
  137. if (!(iss >> addr >> type >> func_name)) {
  138. continue;
  139. }
  140. if (!in_init_section) {
  141. if (func_name == "__init_begin") {
  142. in_init_section = true;
  143. continue;
  144. }
  145. } else if (func_name == "__init_end") {
  146. in_init_section = false;
  147. continue;
  148. }
  149. if (!in_irq_section) {
  150. if (func_name == "__irqentry_text_start") {
  151. in_irq_section = true;
  152. continue;
  153. } else if (func_name == "__irqentry_text_end") {
  154. in_irq_section = false;
  155. continue;
  156. }
  157. } else if (func_name == "__irqentry_text_end") {
  158. in_irq_section = false;
  159. continue;
  160. }
  161. if (func_name.rfind("_kbl_addr_", 0) == 0) {
  162. continue;
  163. }
  164. if (func_name.rfind("__perf", 0) == 0 || func_name.rfind("perf_", 0) == 0) {
  165. continue;
  166. }
  167. if (func_name.rfind("__SCT__", 0) == 0) {
  168. continue;
  169. }
  170. if (std::regex_match(func_name, cold_regex)) {
  171. continue;
  172. }
  173. if ((type == "t" || type == "T" || type == "w" || type == "W") &&
  174. func_name == event_re &&
  175. blacklist.find(func_name) == blacklist.end() &&
  176. avail_filter.find(func_name) != avail_filter.end()) {
  177. fns.insert(func_name);
  178. }
  179. }
  180. return fns;
  181. }
  182. Php::Value php_get_kprobe_functions(Php::Parameters &params) {
  183. std::string fn = params[0].stringValue();
  184. auto res = get_kprobe_functions(fn);
  185. Php::Array result;
  186. for (const auto &item: res) {
  187. result[result.size()] = item;
  188. }
  189. return result;
  190. }
  191. Php::Value php_test(Php::Parameters &params) {
  192. std::string fn = params[0].stringValue();
  193. Php::out << fn << std::endl;
  194. auto res = get_kprobe_functions(fn);
  195. Php::Array result;
  196. for (const auto &item: res) {
  197. result[result.size()] = item;
  198. }
  199. return result;
  200. }
  201. void __construct(Php::Parameters &params) {
  202. std::string source;
  203. if (params.size() == 1 && params[0].isArray()) {
  204. Php::Array opts = params[0];
  205. if (opts.contains("text")) {
  206. source = opts.get("text").stringValue();
  207. } else if (opts.contains("src_file")) {
  208. std::string path = opts.get("src_file");
  209. std::ifstream f(path);
  210. if (!f) {
  211. throw Php::Exception("Failed to open BPF source file: " + path);
  212. }
  213. std::stringstream buffer;
  214. buffer << f.rdbuf();
  215. source = buffer.str();
  216. } else {
  217. throw Php::Exception("Ebpf expects either 'text' or 'src_file' as options.");
  218. }
  219. } else {
  220. throw Php::Exception("Ebpf constructor requires an array with 'text' or 'src_file'.");
  221. }
  222. auto init_res = bpf.init(source);
  223. if (!init_res.ok()) {
  224. throw Php::Exception("init error: " + init_res.msg());
  225. }
  226. this->mod = (void *) bpf.get_mod();
  227. _trace_autoload();
  228. }
  229. ebpf::StatusTuple attach_kfunc(const std::string &kfn) {
  230. int probe_fd;
  231. auto fn = bpf.load_func(kfn, BPF_PROG_TYPE_TRACING, probe_fd);
  232. int res_fd = bpf_attach_kfunc(probe_fd);
  233. if (res_fd < 0) {
  234. TRY2(bpf.unload_func(kfn));
  235. return ebpf::StatusTuple(-1, "Unable to attach kfunc using %s",
  236. kfn.c_str());
  237. }
  238. return ebpf::StatusTuple::OK();
  239. }
  240. ebpf::StatusTuple attach_lsm(const std::string &lsm) {
  241. int probe_fd;
  242. auto fn = bpf.load_func(lsm, BPF_PROG_TYPE_LSM, probe_fd);
  243. int res_fd = bpf_attach_lsm(probe_fd);
  244. if (res_fd < 0) {
  245. TRY2(bpf.unload_func(lsm));
  246. return ebpf::StatusTuple(-1, "Unable to attach lsm using %s",
  247. lsm.c_str());
  248. }
  249. return ebpf::StatusTuple::OK();
  250. }
  251. void php_attach_kprobe(Php::Parameters &params) {
  252. std::string kernel_func = params[0].stringValue();
  253. std::string probe_func = params[1].stringValue();
  254. auto attach_res = bpf.attach_kprobe(kernel_func, probe_func);
  255. if (!attach_res.ok()) {
  256. throw Php::Exception("attach error: " + attach_res.msg());
  257. }
  258. }
  259. void php_attach_tracepoint(Php::Parameters &params) {
  260. std::string tp_func = params[0].stringValue();
  261. std::string probe_func = params[1].stringValue();
  262. auto attach_res = bpf.attach_tracepoint(tp_func, probe_func);
  263. if (!attach_res.ok()) {
  264. throw Php::Exception("attach error: " + attach_res.msg());
  265. }
  266. }
  267. void php_attach_raw_tracepoint(Php::Parameters &params) {
  268. std::string tp_func = params[0].stringValue();
  269. std::string probe_func = params[1].stringValue();
  270. auto attach_res = bpf.attach_raw_tracepoint(tp_func, probe_func);
  271. if (!attach_res.ok()) {
  272. throw Php::Exception("attach error: " + attach_res.msg());
  273. }
  274. }
  275. void php_attach_kfunc(Php::Parameters &params) {
  276. std::string kfunc = params[0].stringValue();
  277. auto attach_res = attach_kfunc(kfunc);
  278. if (!attach_res.ok()) {
  279. throw Php::Exception("attach error: " + attach_res.msg());
  280. }
  281. }
  282. void php_attach_lsm(Php::Parameters &params) {
  283. std::string kfunc = params[0].stringValue();
  284. auto attach_res = attach_lsm(kfunc);
  285. if (!attach_res.ok()) {
  286. throw Php::Exception("attach error: " + attach_res.msg());
  287. }
  288. }
  289. void php_attach_uprobe(Php::Parameters &params) {
  290. std::string binary_path = params[0].stringValue();
  291. std::string symbol = params[1].stringValue();
  292. std::string probe_func = params[2].stringValue();
  293. int64_t symbol_addr = 0, symbol_offset = 0, pid_param;
  294. uint32_t ref_ctr_offset = 0;
  295. pid_t pid = -1;
  296. if (params.size() > 3) {
  297. Php::Value options = params[3];
  298. symbol_addr = options.get("symbol_addr").numericValue();
  299. symbol_offset = options.get("symbol_offset").numericValue();
  300. ref_ctr_offset = options.get("ref_ctr_offset").numericValue();
  301. pid_param = options.get("pid").numericValue();
  302. if (pid_param > 0) {
  303. pid = static_cast<pid_t>(pid_param);
  304. }
  305. }
  306. auto attach_res = bpf.attach_uprobe(binary_path, symbol, probe_func, symbol_addr, BPF_PROBE_ENTRY, pid,
  307. symbol_offset, ref_ctr_offset);
  308. if (!attach_res.ok()) {
  309. throw Php::Exception("attach error: " + attach_res.msg());
  310. }
  311. }
  312. void php_detach_kprobe(Php::Parameters &params) {
  313. std::string fn = params[0].stringValue();
  314. auto detach_res = bpf.detach_kprobe(fn);
  315. if (!detach_res.ok()) {
  316. throw Php::Exception("detach_kprobe error: " + detach_res.msg());
  317. }
  318. }
  319. void php_detach_uprobe(Php::Parameters &params) {
  320. std::string binary_path = params[0].stringValue();
  321. std::string symbol = params[1].stringValue();
  322. int64_t symbol_addr = 0, symbol_offset = 0, pid_param;
  323. pid_t pid = -1;
  324. if (params.size() > 2) {
  325. Php::Value options = params[2];
  326. symbol_addr = options.get("symbol_addr").numericValue();
  327. symbol_offset = options.get("symbol_offset").numericValue();
  328. pid_param = options.get("pid").numericValue();
  329. if (pid_param > 0) {
  330. pid = static_cast<pid_t>(pid_param);
  331. }
  332. }
  333. auto detach_res = bpf.detach_uprobe(binary_path, symbol, symbol_addr, BPF_PROBE_ENTRY, pid, symbol_offset);
  334. if (!detach_res.ok()) {
  335. throw Php::Exception("detach_kprobe error: " + detach_res.msg());
  336. }
  337. }
  338. static void php_trace_print(Php::Parameters &params) {
  339. std::ifstream pipe(TRACE_PIPE_PATH);
  340. if (!pipe.is_open()) {
  341. throw Php::Exception("Failed to open trace_pipe");
  342. }
  343. std::string fmt;
  344. if (params.size() > 0) {
  345. fmt = params[0].stringValue();
  346. }
  347. std::string line;
  348. while (true) {
  349. if (!std::getline(pipe, line)) {
  350. continue;
  351. }
  352. if (line.empty() || line.rfind("CPU:", 0) == 0) {
  353. continue;
  354. }
  355. std::string task = line.substr(0, 16);
  356. task.erase(0, task.find_first_not_of(" "));
  357. std::istringstream iss(line.substr(17));
  358. std::string pid, cpu, flags, ts, msg;
  359. char delim;
  360. if (!(iss >> pid >> delim >> cpu >> delim >> flags >> ts)) {
  361. continue;
  362. }
  363. size_t sym_end = iss.str().find(": ", iss.tellg());
  364. if (sym_end != std::string::npos) {
  365. msg = iss.str().substr(sym_end + 2);
  366. }
  367. std::vector<std::string> fields = {task, pid, cpu, flags, ts, msg};
  368. if (fmt.empty()) {
  369. std::cout << line << std::endl;
  370. } else {
  371. std::string output = fmt;
  372. std::regex pattern(R"(\{(\d+)\})");
  373. std::smatch match;
  374. while (std::regex_search(output, match, pattern)) {
  375. int index = std::stoi(match[1]);
  376. std::string replacement = (index >= 0 && index < (int) fields.size()) ? fields[index] : "";
  377. output.replace(match.position(0), match.length(0), replacement);
  378. }
  379. std::cout << output << std::endl;
  380. }
  381. }
  382. }
  383. static Php::Value php_trace_fields(Php::Parameters &params) {
  384. std::ifstream traceFile(TRACE_PIPE_PATH);
  385. if (!traceFile.is_open()) {
  386. throw Php::Exception("Failed to open trace_pipe");
  387. }
  388. std::string line;
  389. while (std::getline(traceFile, line)) {
  390. if (line.empty()) {
  391. continue;
  392. }
  393. if (line.rfind("CPU:", 0) == 0) {
  394. continue;
  395. }
  396. std::string task = line.substr(0, 16);
  397. task.erase(0, task.find_first_not_of(" "));
  398. std::istringstream iss(line.substr(17));
  399. std::string pid, cpu, flags, ts, msg;
  400. char delim;
  401. if (!(iss >> pid >> delim >> cpu >> delim >> flags >> ts)) {
  402. continue;
  403. }
  404. size_t sym_end = iss.str().find(": ", iss.tellg());
  405. if (sym_end != std::string::npos) {
  406. msg = iss.str().substr(sym_end + 2);
  407. }
  408. Php::Array result;
  409. result[0] = task;
  410. result[1] = std::stoi(pid);
  411. result[2] = std::stoi(cpu.substr(1, cpu.size() - 2));
  412. result[3] = flags;
  413. result[4] = std::stod(ts);
  414. result[5] = Php::Value(msg.c_str(), msg.size());
  415. return result;
  416. }
  417. return Php::Value();
  418. }
  419. // Php::Value php_perf_event(Php::Parameters &params) {
  420. // std::string event_name = params[0].stringValue(); // Get event_name from the parameters
  421. // this->_class_perf_event = new PerfEventArrayTable(&this->bpf, event_name);
  422. // return Php::Object("PerfEventArrayTable", this->_class_perf_event);
  423. // }
  424. Php::Value get_table_cls(const char *table_name, int from_attr) {
  425. int ttype = bpf_table_type(this->mod, table_name);
  426. switch (ttype) {
  427. case BPF_MAP_TYPE_HASH:
  428. return Php::Object("HashTable", new HashTable(&this->bpf, table_name));
  429. case BPF_MAP_TYPE_ARRAY:
  430. return Php::Object("ArrayTable", new ArrayTable(&this->bpf, table_name));
  431. case BPF_MAP_TYPE_PROG_ARRAY:
  432. return Php::Object("ProgArrayTable", new ProgArrayTable(&this->bpf, table_name));
  433. case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
  434. if (_class_perf_event_obj.isNull()) {
  435. _class_perf_event_obj = Php::Object("PerfEventArrayTable",
  436. new PerfEventArrayTable(&this->bpf, table_name));
  437. }
  438. return _class_perf_event_obj;
  439. case BPF_MAP_TYPE_PERCPU_HASH:
  440. return Php::Object("PerCpuHashTable", new PerCpuHashTable(&this->bpf, table_name));
  441. case BPF_MAP_TYPE_PERCPU_ARRAY:
  442. return Php::Object("PerCpuArrayTable", new PerCpuArrayTable(&this->bpf, table_name));
  443. case BPF_MAP_TYPE_LPM_TRIE:
  444. return Php::Object("LpmTrieTable", new LpmTrieTable(&this->bpf, table_name));
  445. case BPF_MAP_TYPE_STACK_TRACE:
  446. return Php::Object("StackTraceTable", new StackTraceTable(&this->bpf, table_name));
  447. case BPF_MAP_TYPE_LRU_HASH:
  448. return Php::Object("LruHashTable", new LruHashTable(&this->bpf, table_name));
  449. case BPF_MAP_TYPE_LRU_PERCPU_HASH:
  450. return Php::Object("LruPerCpuHashTable", new LruPerCpuHashTable(&this->bpf, table_name));
  451. case BPF_MAP_TYPE_CGROUP_ARRAY:
  452. return Php::Object("CgroupArrayTable", new CgroupArrayTable(&this->bpf, table_name));
  453. case BPF_MAP_TYPE_DEVMAP:
  454. return Php::Object("DevMapTable", new DevMapTable(&this->bpf, table_name));
  455. case BPF_MAP_TYPE_CPUMAP:
  456. return Php::Object("CpuMapTable", new CpuMapTable(&this->bpf, table_name));
  457. case BPF_MAP_TYPE_XSKMAP:
  458. return Php::Object("XskMapTable", new XskMapTable(&this->bpf, table_name));
  459. case BPF_MAP_TYPE_ARRAY_OF_MAPS:
  460. return Php::Object("MapInMapArrayTable", new MapInMapArrayTable(&this->bpf, table_name));
  461. case BPF_MAP_TYPE_HASH_OF_MAPS:
  462. return Php::Object("MapInMapHashTable", new MapInMapHashTable(&this->bpf, table_name));
  463. case BPF_MAP_TYPE_QUEUE:
  464. case BPF_MAP_TYPE_STACK:
  465. return Php::Object("QueueStackTable", new QueueStackTable(&this->bpf, table_name));
  466. case BPF_MAP_TYPE_RINGBUF:
  467. return Php::Object("RingBufTable", new RingBufTable(&this->bpf, table_name));
  468. default:
  469. if (from_attr) {
  470. return ttype;
  471. }
  472. throw Php::Exception("Unknown table type " + std::to_string(ttype));
  473. }
  474. }
  475. Php::Value php_get_table(Php::Parameters &params) {
  476. std::string table_name = params[0].stringValue();
  477. int from_fn = 0;
  478. return get_table_cls(table_name.c_str(), from_fn);
  479. }
  480. void php_perf_buffer_poll(Php::Parameters &params) {
  481. if (_class_perf_event_obj.isNull()) {
  482. throw Php::Exception("perf event is null.");
  483. }
  484. auto *_class_perf_event = (PerfEventArrayTable *) _class_perf_event_obj.implementation();
  485. if (!_class_perf_event) {
  486. throw Php::Exception("perf buffer poll error1.");
  487. }
  488. int timeout_ms = -1;
  489. int res = _class_perf_event->perf_buffer_poll(timeout_ms);
  490. if (res < 0) {
  491. throw Php::Exception("perf buffer poll error.");
  492. }
  493. }
  494. Php::Value php_get_syscall_fnname(Php::Parameters &params) {
  495. std::string name = params[0].stringValue();
  496. return bpf.get_syscall_fnname(name);
  497. }
  498. Php::Value php_load_func(Php::Parameters &params) {
  499. std::string fn = params[0].stringValue();
  500. auto prog_type = static_cast<bpf_prog_type>(params[1].numericValue());
  501. int probe_fd;
  502. auto res = bpf.load_func(fn, prog_type, probe_fd);
  503. if (res.ok()) {
  504. Php::Object prog_fn = Php::Object("BPFProgFunction", new ProgFunc(&this->bpf, fn, probe_fd));
  505. prog_fn.set("name", fn);
  506. prog_fn.set("fd", probe_fd);
  507. return prog_fn;
  508. } else {
  509. throw Php::Exception(res.msg());
  510. }
  511. }
  512. void php_attach_raw_socket(Php::Parameters &params) {
  513. if (!params[0].isObject()) {
  514. throw Php::Exception("First parameter must be a BPFProgFunction object.");
  515. }
  516. Php::Object prog_fn = params[0];
  517. std::string interface = params[1].stringValue();
  518. auto *prog = static_cast<ProgFunc *>(prog_fn.implementation());
  519. if (prog == nullptr) {
  520. throw Php::Exception("Invalid BPFProgFunction object.");
  521. }
  522. int sock = bpf_open_raw_sock(interface.c_str());
  523. if (sock < 0) {
  524. throw Php::Exception("Failed to open raw socket on interface: " + interface);
  525. }
  526. auto res = bpf_attach_socket(sock, prog->_fd);
  527. if (res < 0) {
  528. throw Php::Exception("Failed to attach BPF program to socket.");
  529. }
  530. }
  531. };
  532. std::string sanitize_str(std::string str, bool (*validator)(char),
  533. char replacement = '_') {
  534. for (size_t i = 0; i < str.length(); i++)
  535. if (!validator(str[i]))
  536. str[i] = replacement;
  537. return str;
  538. }
  539. std::string attach_type_prefix(bpf_probe_attach_type type) {
  540. switch (type) {
  541. case BPF_PROBE_ENTRY:
  542. return "p";
  543. case BPF_PROBE_RETURN:
  544. return "r";
  545. }
  546. return "ERROR";
  547. }
  548. static bool kprobe_event_validator(char c) {
  549. return (c != '+') && (c != '.');
  550. }
  551. std::string get_kprobe_event(const std::string &kernel_func,
  552. bpf_probe_attach_type type) {
  553. std::string res = attach_type_prefix(type) + "_";
  554. res += sanitize_str(kernel_func, &kprobe_event_validator);
  555. return res;
  556. }
  557. extern "C" {
  558. PHPCPP_EXPORT void *get_module() {
  559. static Php::Extension extension("ebpf", "1.0.0");
  560. Php::Class<EbpfExtension> ebpf_class("Bpf");
  561. ebpf_class.method<&EbpfExtension::php_trace_print>("trace_print", {
  562. Php::ByVal("fmt", Php::Type::String, false)
  563. });
  564. ebpf_class.method<&EbpfExtension::php_trace_fields>("trace_fields");
  565. ebpf_class.method<&EbpfExtension::__construct>("__construct", {
  566. Php::ByVal("opt", Php::Type::Array)
  567. });
  568. ebpf_class.method<&EbpfExtension::php_attach_kprobe>("attach_kprobe", {
  569. Php::ByVal("kernel_func", Php::Type::String),
  570. Php::ByVal("probe_func", Php::Type::String)
  571. });
  572. ebpf_class.method<&EbpfExtension::php_attach_tracepoint>("attach_tracepoint", {
  573. Php::ByVal("tp", Php::Type::String),
  574. Php::ByVal("probe_func", Php::Type::String)
  575. });
  576. ebpf_class.method<&EbpfExtension::php_attach_raw_tracepoint>("attach_raw_tracepoint", {
  577. Php::ByVal("tp", Php::Type::String),
  578. Php::ByVal("probe_func", Php::Type::String)
  579. });
  580. ebpf_class.method<&EbpfExtension::php_attach_kfunc>("attach_kfunc", {
  581. Php::ByVal("kfn", Php::Type::String),
  582. });
  583. ebpf_class.method<&EbpfExtension::php_attach_lsm>("attach_lsm", {
  584. Php::ByVal("lsm", Php::Type::String),
  585. });
  586. ebpf_class.method<&EbpfExtension::php_attach_uprobe>("attach_uprobe", {
  587. Php::ByVal("binary_path", Php::Type::String),
  588. Php::ByVal("symbol", Php::Type::String),
  589. Php::ByVal("probe_func", Php::Type::String),
  590. Php::ByVal("options", Php::Type::Array, false),
  591. });
  592. // ebpf_class.method<&EbpfExtension::php_perf_event>("perf_event", {
  593. // Php::ByVal("ev_name", Php::Type::String),
  594. // });
  595. ebpf_class.method<&EbpfExtension::php_get_table>("get_table", {
  596. Php::ByVal("tb_name", Php::Type::String),
  597. });
  598. ebpf_class.method<&EbpfExtension::php_perf_buffer_poll>("perf_buffer_poll", {});
  599. ebpf_class.method<&EbpfExtension::php_get_syscall_fnname>("get_syscall_fnname", {
  600. Php::ByVal("name", Php::Type::String),
  601. });
  602. /*detach*/
  603. ebpf_class.method<&EbpfExtension::php_detach_kprobe>("detach_kprobe", {
  604. Php::ByVal("kernel_func", Php::Type::String),
  605. });
  606. ebpf_class.method<&EbpfExtension::php_detach_uprobe>("detach_uprobe", {
  607. Php::ByVal("binary_path", Php::Type::String),
  608. Php::ByVal("symbol", Php::Type::String),
  609. Php::ByVal("options", Php::Type::Array, false),
  610. });
  611. ebpf_class.method<&EbpfExtension::php_test>("test", {
  612. Php::ByVal("idx", Php::Type::String),
  613. });
  614. ebpf_class.method<&EbpfExtension::php_get_kprobe_functions>("get_kprobe_functions", {
  615. Php::ByVal("fn", Php::Type::String),
  616. });
  617. ebpf_class.method<&EbpfExtension::php_load_func>("load_func", {
  618. Php::ByVal("fn", Php::Type::String),
  619. Php::ByVal("type", Php::Type::Numeric),
  620. });
  621. ebpf_class.method<&EbpfExtension::php_attach_raw_socket>("attach_raw_socket", {
  622. // Php::ByVal("fn", Php::Type::Object),
  623. // Php::ByVal("interface", Php::Type::String),
  624. });
  625. /*HashTable*/
  626. Php::Class<HashTable> ebpf_hashtable_cls("HashTable");
  627. ebpf_hashtable_cls.method<&HashTable::php_get_values>("values", {});
  628. ebpf_hashtable_cls.method<&HashTable::php_clear>("clear",{});
  629. /*ArrayTable*/
  630. Php::Class<ArrayTable> ebpf_array_table_cls("ArrayTable");
  631. ebpf_array_table_cls.method<&ArrayTable::php_get_value>("get_value", {
  632. Php::ByVal("idx", Php::Type::Numeric),
  633. });
  634. /*ProgArrayTable*/
  635. Php::Class<ProgArrayTable> ebpf_prog_array_table_cls("ProgArrayTable");
  636. /*PerfEventArrayTable*/
  637. Php::Class<PerfEventArrayTable> ebpf_perf_event_class("PerfEventArrayTable");
  638. ebpf_perf_event_class.method<&PerfEventArrayTable::php_open_perf_buffer>("open_perf_buffer", {
  639. Php::ByVal("fn", Php::Type::String),
  640. });
  641. /*PerCpuHashTable*/
  642. Php::Class<PerCpuHashTable> ebpf_percpuhash_table_cls("PerCpuHashTable");
  643. /*PerCpuArrayTable*/
  644. Php::Class<PerCpuArrayTable> ebpf_per_cpu_array_table_cls("PerCpuArrayTable");
  645. ebpf_per_cpu_array_table_cls.method<&PerCpuArrayTable::php_sum_value>("sum_value", {
  646. Php::ByVal("idx", Php::Type::Numeric),
  647. });
  648. /*LpmTrieTable*/
  649. Php::Class<LpmTrieTable> ebpf_lpmtrie_table_cls("LpmTrieTable");
  650. /*StackTraceTable*/
  651. Php::Class<StackTraceTable> ebpf_stack_trace_table_cls("StackTraceTable");
  652. ebpf_stack_trace_table_cls.method<&StackTraceTable::php_get_values>("values", {
  653. Php::ByVal("stack", Php::Type::Numeric),
  654. Php::ByVal("pid", Php::Type::Numeric, false),
  655. });
  656. /*LruHashTable*/
  657. Php::Class<LruHashTable> ebpf_lruhash_table_cls("LruHashTable");
  658. /*LruPerCpuHashTable*/
  659. Php::Class<LruPerCpuHashTable> ebpf_lruper_cpuhash_table_cls("LruPerCpuHashTable");
  660. /*CgroupArrayTable*/
  661. Php::Class<CgroupArrayTable> ebpf_cgroup_array_table_cls("CgroupArrayTable");
  662. /*DevMapTable*/
  663. Php::Class<DevMapTable> ebpf_devmap_table_cls("DevMapTable");
  664. /*CpuMapTable*/
  665. Php::Class<CpuMapTable> ebpf_cpumap_table_cls("CpuMapTable");
  666. /*XskMapTable*/
  667. Php::Class<XskMapTable> ebpf_xskmap_table_cls("XskMapTable");
  668. /*MapInMapArrayTable*/
  669. Php::Class<MapInMapArrayTable> ebpf_map_in_map_array_table_cls("MapInMapArrayTable");
  670. /*MapInMapHashTable*/
  671. Php::Class<MapInMapHashTable> ebpf_map_in_maphash_table_cls("MapInMapHashTable");
  672. /*QueueStackTable*/
  673. Php::Class<QueueStackTable> ebpf_queue_stack_table_cls("QueueStackTable");
  674. /*RingBufTable*/
  675. Php::Class<RingBufTable> ebpf_ringbuf_table_cls("RingBufTable");
  676. /*ProgFunc*/
  677. Php::Class<ProgFunc> ebpf_prog_func_cls("BPFProgFunction");
  678. extension.add(std::move(ebpf_prog_func_cls));
  679. /* const */
  680. ebpf_class.constant("SOCKET_FILTER", BPFProgType::SOCKET_FILTER);
  681. ebpf_class.constant("KPROBE", BPFProgType::KPROBE);
  682. ebpf_class.constant("SCHED_CLS", BPFProgType::SCHED_CLS);
  683. ebpf_class.constant("SCHED_ACT", BPFProgType::SCHED_ACT);
  684. ebpf_class.constant("TRACEPOINT", BPFProgType::TRACEPOINT);
  685. ebpf_class.constant("XDP", BPFProgType::XDP);
  686. ebpf_class.constant("PERF_EVENT", BPFProgType::PERF_EVENT);
  687. ebpf_class.constant("CGROUP_SKB", BPFProgType::CGROUP_SKB);
  688. ebpf_class.constant("CGROUP_SOCK", BPFProgType::CGROUP_SOCK);
  689. ebpf_class.constant("LWT_IN", BPFProgType::LWT_IN);
  690. ebpf_class.constant("LWT_OUT", BPFProgType::LWT_OUT);
  691. ebpf_class.constant("LWT_XMIT", BPFProgType::LWT_XMIT);
  692. ebpf_class.constant("SOCK_OPS", BPFProgType::SOCK_OPS);
  693. ebpf_class.constant("SK_SKB", BPFProgType::SK_SKB);
  694. ebpf_class.constant("CGROUP_DEVICE", BPFProgType::CGROUP_DEVICE);
  695. ebpf_class.constant("SK_MSG", BPFProgType::SK_MSG);
  696. ebpf_class.constant("RAW_TRACEPOINT", BPFProgType::RAW_TRACEPOINT);
  697. ebpf_class.constant("CGROUP_SOCK_ADDR", BPFProgType::CGROUP_SOCK_ADDR);
  698. ebpf_class.constant("CGROUP_SOCKOPT", BPFProgType::CGROUP_SOCKOPT);
  699. ebpf_class.constant("TRACING", BPFProgType::TRACING);
  700. ebpf_class.constant("LSM", BPFProgType::LSM);
  701. extension.add(std::move(ebpf_hashtable_cls));
  702. extension.add(std::move(ebpf_array_table_cls));
  703. extension.add(std::move(ebpf_prog_array_table_cls));
  704. extension.add(std::move(ebpf_perf_event_class));
  705. extension.add(std::move(ebpf_percpuhash_table_cls));
  706. extension.add(std::move(ebpf_per_cpu_array_table_cls));
  707. extension.add(std::move(ebpf_lpmtrie_table_cls));
  708. extension.add(std::move(ebpf_stack_trace_table_cls));
  709. extension.add(std::move(ebpf_lruhash_table_cls));
  710. extension.add(std::move(ebpf_lruper_cpuhash_table_cls));
  711. extension.add(std::move(ebpf_cgroup_array_table_cls));
  712. extension.add(std::move(ebpf_devmap_table_cls));
  713. extension.add(std::move(ebpf_cpumap_table_cls));
  714. extension.add(std::move(ebpf_xskmap_table_cls));
  715. extension.add(std::move(ebpf_map_in_map_array_table_cls));
  716. extension.add(std::move(ebpf_map_in_maphash_table_cls));
  717. extension.add(std::move(ebpf_queue_stack_table_cls));
  718. extension.add(std::move(ebpf_ringbuf_table_cls));
  719. extension.add(std::move(ebpf_class));
  720. return extension;
  721. }
  722. }