1 /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
2 /* Copyright (c) 2021 Hengqi Chen */
3 #include <vmlinux.h>
4 #include <bpf/bpf_helpers.h>
5 #include <bpf/bpf_core_read.h>
6 #include <bpf/bpf_tracing.h>
7 #include <bpf/bpf_endian.h>
8 #include "bindsnoop.h"
9
10 #define MAX_ENTRIES 10240
11 #define MAX_PORTS 1024
12
13 const volatile pid_t target_pid = 0;
14 const volatile bool ignore_errors = true;
15 const volatile bool filter_by_port = false;
16
17 struct {
18 __uint(type, BPF_MAP_TYPE_HASH);
19 __uint(max_entries, MAX_ENTRIES);
20 __type(key, __u32);
21 __type(value, struct socket *);
22 } sockets SEC(".maps");
23
24 struct {
25 __uint(type, BPF_MAP_TYPE_HASH);
26 __uint(max_entries, MAX_PORTS);
27 __type(key, __u16);
28 __type(value, __u16);
29 } ports SEC(".maps");
30
31 struct {
32 __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
33 __uint(key_size, sizeof(__u32));
34 __uint(value_size, sizeof(__u32));
35 } events SEC(".maps");
36
probe_entry(struct pt_regs * ctx,struct socket * socket)37 static int probe_entry(struct pt_regs *ctx, struct socket *socket)
38 {
39 __u64 pid_tgid = bpf_get_current_pid_tgid();
40 __u32 pid = pid_tgid >> 32;
41 __u32 tid = (__u32)pid_tgid;
42
43 if (target_pid && target_pid != pid)
44 return 0;
45
46 bpf_map_update_elem(&sockets, &tid, &socket, BPF_ANY);
47 return 0;
48 };
49
probe_exit(struct pt_regs * ctx,short ver)50 static int probe_exit(struct pt_regs *ctx, short ver)
51 {
52 __u64 pid_tgid = bpf_get_current_pid_tgid();
53 __u32 pid = pid_tgid >> 32;
54 __u32 tid = (__u32)pid_tgid;
55 struct socket **socketp, *socket;
56 struct inet_sock *inet_sock;
57 struct sock *sock;
58 union bind_options opts;
59 struct bind_event event = {};
60 __u16 sport = 0, *port;
61 int ret;
62
63 socketp = bpf_map_lookup_elem(&sockets, &tid);
64 if (!socketp)
65 return 0;
66
67 ret = PT_REGS_RC(ctx);
68 if (ignore_errors && ret != 0)
69 goto cleanup;
70
71 socket = *socketp;
72 sock = BPF_CORE_READ(socket, sk);
73 inet_sock = (struct inet_sock *)sock;
74
75 sport = bpf_ntohs(BPF_CORE_READ(inet_sock, inet_sport));
76 port = bpf_map_lookup_elem(&ports, &sport);
77 if (filter_by_port && !port)
78 goto cleanup;
79
80 opts.fields.freebind = BPF_CORE_READ_BITFIELD_PROBED(inet_sock, freebind);
81 opts.fields.transparent = BPF_CORE_READ_BITFIELD_PROBED(inet_sock, transparent);
82 opts.fields.bind_address_no_port = BPF_CORE_READ_BITFIELD_PROBED(inet_sock, bind_address_no_port);
83 opts.fields.reuseaddress = BPF_CORE_READ_BITFIELD_PROBED(sock, __sk_common.skc_reuse);
84 opts.fields.reuseport = BPF_CORE_READ_BITFIELD_PROBED(sock, __sk_common.skc_reuseport);
85 event.opts = opts.data;
86 event.ts_us = bpf_ktime_get_ns() / 1000;
87 event.pid = pid;
88 event.port = sport;
89 event.bound_dev_if = BPF_CORE_READ(sock, __sk_common.skc_bound_dev_if);
90 event.ret = ret;
91 event.proto = BPF_CORE_READ_BITFIELD_PROBED(sock, sk_protocol);
92 bpf_get_current_comm(&event.task, sizeof(event.task));
93 if (ver == 4) {
94 event.ver = ver;
95 bpf_probe_read_kernel(&event.addr, sizeof(event.addr), &inet_sock->inet_saddr);
96 } else { /* ver == 6 */
97 event.ver = ver;
98 bpf_probe_read_kernel(&event.addr, sizeof(event.addr), sock->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32);
99 }
100 bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event));
101
102 cleanup:
103 bpf_map_delete_elem(&sockets, &tid);
104 return 0;
105 }
106
107 SEC("kprobe/inet_bind")
BPF_KPROBE(ipv4_bind_entry,struct socket * socket)108 int BPF_KPROBE(ipv4_bind_entry, struct socket *socket)
109 {
110 return probe_entry(ctx, socket);
111 }
112
113 SEC("kretprobe/inet_bind")
BPF_KRETPROBE(ipv4_bind_exit)114 int BPF_KRETPROBE(ipv4_bind_exit)
115 {
116 return probe_exit(ctx, 4);
117 }
118
119 SEC("kprobe/inet6_bind")
BPF_KPROBE(ipv6_bind_entry,struct socket * socket)120 int BPF_KPROBE(ipv6_bind_entry, struct socket *socket)
121 {
122 return probe_entry(ctx, socket);
123 }
124
125 SEC("kretprobe/inet6_bind")
BPF_KRETPROBE(ipv6_bind_exit)126 int BPF_KRETPROBE(ipv6_bind_exit)
127 {
128 return probe_exit(ctx, 6);
129 }
130
131 char LICENSE[] SEC("license") = "Dual BSD/GPL";
132