ebpf 网络过滤器实践

本文介绍了EBPF(Extended Berkeley Packet Filter)在网络监控中的应用,通过实例展示了如何编写EBPF程序来统计网络包,并分析了不同返回值对RAW SOCKET数据包接收的影响。实践部分包括统计网络流量和过滤数据包,揭示了EBPF在流量监控和数据过滤方面的功能。

一、前言

不要认为是一些小的demo而忽略对它的学习,往往一个复杂的代码,复杂的工程是一个个小demo拼接成的。

ebpf 网络过滤器这门技术在网络监控领域用的非常多,ebpf 编程我现在用的比较多的其实只有几个探针,kprobe、uprobe 以及linux网络过滤器。

今天读了 <<Linux内核观测技术BPF>>以及linux 内核源码中的sock_user1.c sock_kern.c ,linux内核的demo我认为写的还是很简单,而且这本书里面写的也很简单,打算对书里和linux内核的demo里的例子做一下实践。

内核代码很复杂,以后有空再看,今晚对linux 网络过滤器这块部分进行了实践。

心中一直有个疑问,好多网文都说他是cbpf的革新,那么cbpf filter 在pcap 中有过滤数据包能力,那ebpf是如何做到的呢?

二、实践

场景1:ebpf 统计网络包

ebpf程序

// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright (c) 2020 Facebook */
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
/* Packet types */

/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#ifndef __BPF_LEGACY__
#define __BPF_LEGACY__

/* llvm builtin functions that eBPF C program may use to
 * emit BPF_LD_ABS and BPF_LD_IND instructions
 */
unsigned long long load_byte(void *skb,
			     unsigned long long off) asm("llvm.bpf.load.byte");
unsigned long long load_half(void *skb,
			     unsigned long long off) asm("llvm.bpf.load.half");
unsigned long long load_word(void *skb,
			     unsigned long long off) asm("llvm.bpf.load.word");

#endif


#define PACKET_OUTGOING		4		/* Outgoing of any type */


#define ETH_ALEN	6		/* Octets in one ethernet addr	 */
#define ETH_TLEN	2		/* Octets in ethernet type field */
#define ETH_HLEN	14		/* Total octets in header.	 */
#define ETH_ZLEN	60		/* Min. octets in frame sans FCS */
#define ETH_DATA_LEN	1500		/* Max. octets in payload	 */
#define ETH_FRAME_LEN	1514		/* Max. octets in frame sans FCS */
#define ETH_FCS_LEN	4		/* Octets in the FCS		 */
 
char LICENSE[] SEC("license") = "Dual BSD/GPL";
 
struct {
	__uint(type, BPF_MAP_TYPE_ARRAY);
	__type(key, u32);
	__type(value, long);
	__uint(max_entries, 256);
} my_map SEC(".maps");

SEC("socket/test")
int bpf_prog1(struct __sk_buff *skb)
{
	int index = load_byte(skb, ETH_HLEN + offsetof(struct iphdr, protocol));
	long *value;

	if (skb->pkt_type != PACKET_OUTGOING)
		return -1;

	value = bpf_map_lookup_elem(&my_map, &index);
	if (value)
		__sync_fetch_and_add(value, skb->len);


	return 0;
}
char _license[] SEC("license") = "GPL";

编译ebpf程序:

/usr/bin/clang-14 -g -O2 -target bpf  -D__TARGET_ARCH_x86_64 -c kern1.c -o kernel_write.o

user 部分程序

其实很简单 创建一个链路套接字,然后把我们的bpf程序使用SO_ATTACH_BPF附着上去就可以了

common.h

/* SPDX-License-Identifier: GPL-2.0 */
#include <stdlib.h>
#include <stdio.h>
#include <linux/unistd.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <linux/if_ether.h>
#include <net/if.h>
#include <linux/if_packet.h>
#include <arpa/inet.h>

static inline 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) {
		printf("cannot 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(s
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值