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
|
#include <argp.h>
#include <arpa/inet.h>
#include <assert.h>
#include <bpf/libbpf.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#include <linux/in.h>
#include <net/if.h>
#include <signal.h>
#include <stdio.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <unistd.h>
#include "sockfilter.h"
#include "sockfilter.skel.h"
// 存储要监听的端口
static struct env {
const char *interface;
} env;
// 协议格式与协议名的映射表
static const char *ipproto_mapping[IPPROTO_MAX] = {
[IPPROTO_IP] = "IP", [IPPROTO_ICMP] = "ICMP", [IPPROTO_IGMP] = "IGMP",
[IPPROTO_IPIP] = "IPIP", [IPPROTO_TCP] = "TCP", [IPPROTO_EGP] = "EGP",
[IPPROTO_PUP] = "PUP", [IPPROTO_UDP] = "UDP", [IPPROTO_IDP] = "IDP",
[IPPROTO_TP] = "TP", [IPPROTO_DCCP] = "DCCP", [IPPROTO_IPV6] = "IPV6",
[IPPROTO_RSVP] = "RSVP", [IPPROTO_GRE] = "GRE", [IPPROTO_ESP] = "ESP",
[IPPROTO_AH] = "AH", [IPPROTO_MTP] = "MTP", [IPPROTO_BEETPH] = "BEETPH",
[IPPROTO_ENCAP] = "ENCAP", [IPPROTO_PIM] = "PIM", [IPPROTO_COMP] = "COMP",
[IPPROTO_SCTP] = "SCTP", [IPPROTO_UDPLITE] = "UDPLITE", [IPPROTO_MPLS] = "MPLS",
[IPPROTO_RAW] = "RAW"
};
// 创建并绑定一个原始套接字到指定的网络接口
static int open_raw_sock(const char *name)
{
struct sockaddr_ll sll;
int sock;
// 创建一个原始套接字
sock = socket(PF_PACKET, SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC, htons(ETH_P_ALL));
if (sock < 0) {
fprintf(stderr, "Failed to create raw socket\n");
return -1;
}
// 设置套接字,并绑定套接字到指定的网络接口
memset(&sll, 0, sizeof(sll));
sll.sll_family = AF_PACKET;
sll.sll_ifindex = if_nametoindex(name);
sll.sll_protocol = htons(ETH_P_ALL);
if (bind(sock, (struct sockaddr *)&sll, sizeof(sll)) < 0) {
fprintf(stderr, "Failed to bind to %s: %s\n", name, strerror(errno));
close(sock);
return -1;
}
return sock;
}
// libbpf日志输出的回调函数
static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
{
return vfprintf(stderr, format, args);
}
// 将网络字节序的IP地址为点分十进制字符串格式
static inline void ltoa(uint32_t addr, char *dst)
{
snprintf(dst, 16, "%u.%u.%u.%u", (addr >> 24) & 0xFF, (addr >> 16) & 0xFF,
(addr >> 8) & 0xFF, (addr & 0xFF));
}
// 接收到事件数据的回调函数
static int handle_event(void *ctx, void *data, size_t data_sz)
{
const struct so_event *e = data;
char ifname[IF_NAMESIZE];
char sstr[16] = {}, dstr[16] = {};
if (e->pkt_type != PACKET_HOST)
return 0;
if (e->ip_proto < 0 || e->ip_proto >= IPPROTO_MAX)
return 0;
// 通过接口索引获取接口名称
if (!if_indextoname(e->ifindex, ifname))
return 0;
// 格式化处理源IP地址与目的IP地址
ltoa(ntohl(e->src_addr), sstr);
ltoa(ntohl(e->dst_addr), dstr);
// 输出数据包信息
printf("interface: %s\tprotocol: %s\t%s:%d(src) -> %s:%d(dst)\n", ifname,
ipproto_mapping[e->ip_proto], sstr, ntohs(e->port16[0]), dstr, ntohs(e->port16[1]));
return 0;
}
// 标记是否结束进程
static volatile bool exiting = false;
// 信号处理的回调函数
static void sig_handler(int sig)
{
exiting = true;
}
int main(int argc, char **argv)
{
struct ring_buffer *rb = NULL;
struct sockfilter_bpf *skel;
int err, prog_fd, sock;
// 监听环回端口
env.interface = "lo";
// 设置libbpf日志输出的回调函数
libbpf_set_print(libbpf_print_fn);
// 设置信号处理的回调函数
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
// 加载eBPF程序
skel = sockfilter_bpf__open_and_load();
if (!skel) {
fprintf(stderr, "Failed to open and load BPF skeleton\n");
return 1;
}
// 创建Ring Buffer事件监听对象,设置handle_event作为接收事件的回调函数
rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event, NULL, NULL);
if (!rb) {
err = -1;
fprintf(stderr, "Failed to create ring buffer\n");
goto cleanup;
}
// 创建原始套接字
sock = open_raw_sock(env.interface);
if (sock < 0) {
err = -2;
fprintf(stderr, "Failed to open raw socket\n");
goto cleanup;
}
// 使用setsockopt函数将eBPF程序附加到上面已经创建的套接字sock上
prog_fd = bpf_program__fd(skel->progs.socket_handler);
if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd))) {
err = -3;
fprintf(stderr, "Failed to attach to raw socket\n");
goto cleanup;
}
// 主循环
while (!exiting) {
// 不断从eBPF程序拉取事件信息
err = ring_buffer__poll(rb, 100);
if (err == -EINTR) {
err = 0;
break;
}
if (err < 0) {
fprintf(stderr, "Error polling perf buffer: %d\n", err);
break;
}
sleep(1);
}
cleanup:
// 释放Ring Buffer
ring_buffer__free(rb);
// 卸载清理eBPF环境
sockfilter_bpf__destroy(skel);
return -err;
}
|