原文链接地址 https://mp.weixin.qq.com/s/xclWzPrAWQ0cxe7Md-c5_g
BPF的逻辑如下图:
![](https://i-blog.csdnimg.cn/blog_migrate/86ce420087185f55417448afff25322e.jpeg)
原文中源代码如下:
#!/usr/bin/env python3
import dnslib
import fcntl
import os
import sys
from bcc import BPF
BPF_APP = r'''
#include <linux/if_ether.h>
#include <linux/in.h>
#include <bcc/proto.h>
int dns_matching(struct __sk_buff *skb) {
u8 *cursor = 0;
// Checking the IP protocol:
struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));
if (ethernet->type == ETH_P_IP) {
// Checking the UDP protocol:
struct ip_t *ip = cursor_advance(cursor, sizeof(*ip));
if (ip->nextp == IPPROTO_UDP) {
// Check the port 53:
struct udp_t *udp = cursor_advance(cursor, sizeof(*udp));
if (udp->dport == 53 || udp->sport == 53) {
return -1;
}
}
}
return 0;
}
'''
bpf = BPF(text=BPF_APP)
function_dns_matching = bpf.load_func("dns_matching", BPF.SOCKET_FILTER)
BPF.attach_raw_socket(function_dns_matching, '')
socket_fd = function_dns_matching.sock
fl = fcntl.fcntl(socket_fd, fcntl.F_GETFL)
fcntl.fcntl(socket_fd, fcntl.F_SETFL, fl & ~os.O_NONBLOCK)
while True:
try:
packet_str = os.read(socket_fd, 2048)
except KeyboardInterrupt:
sys.exit(0)
packet_bytearray = bytearray(packet_str)
ETH_HLEN = 14
UDP_HLEN = 8
# IP header length
ip_header_length = packet_bytearray[ETH_HLEN]
ip_header_length = ip_header_length & 0x0F
ip_header_length = ip_header_length << 2
# Starting the DNS packet
payload_offset = ETH_HLEN + ip_header_length + UDP_HLEN
payload = packet_bytearray[payload_offset:]
dnsrec = dnslib.DNSRecord.parse(payload)
# If it’s the response:
if dnsrec.rr:
print(f'Resp: {dnsrec.rr[0].rname} {dnslib.QTYPE.get(dnsrec.rr[0].rtype)} {", ".join([repr(dnsrec.rr[i].rdata) for i in range(0, len(dnsrec.rr))])}')
# If it’s the request:
else:
print(f'Request: {dnsrec.questions[0].qname} {dnslib.QTYPE.get(dnsrec.questions[0].qtype)}')
BPF_APP定义了【BPF Program】,该【BPF Program】程序段中定义了int dns_matching(struct __sk_buff *skb);函数,该函数是说检测到UDP源端口或目的端口为53,即DNS的request或response数据包时,【BPF Program】return -1。
【BPF Program】里cursor_advance的解释如下:
cursor_advance宏可以在包的范围内移动光标(指针),返回其当前位置并移动到指定位置。
Python3里的BPF函数帮助信息
>>> from bcc import BPF
>>> help(BPF)
Help on class BPF in module bcc:
class BPF(builtins.object)
| BPF(src_file=b'', hdr_file=b'', text=None, debug=0, cflags=[], usdt_contexts=[], allow_rlimit=True, device=None)
|
| Methods defined here:
|
| Table(bpf, map_id, map_fd, keytype, leaftype, name, **kwargs)
| Table(bpf, map_id, map_fd, keytype, leaftype, **kwargs)
|
| Create a python object out of a reference to a bpf table handle
|
|
| __init__(self, src_file=b'', hdr_file=b'', text=None, debug=0, cflags=[], usdt_contexts=[], allow_rlimit=True, device=None)
| Create a new BPF module with the given source code.
|
| Note:
| All fields are marked as optional, but either `src_file` or `text`
| must be supplied, and not both.
|
| Args:
| src_file (Optional[str]): Path to a source file for the module
| hdr_file (Optional[str]): Path to a helper header file for the `src_file`
| text (Optional[str]): Contents of a source file for the module
| debug (Optional[int]): Flags used for debug prints, can be |'d together
| See "Debug flags" for explanation
| load_func(self, func_name, prog_type, device=None)
|
| load_funcs(self, prog_type=2)
| load_funcs(prog_type=KPROBE)
|
| Load all functions in this BPF module with the given type.
| Returns a list of the function handles.
|
.......