Skip to content

Commit

Permalink
Add sample usage for BPF_PROG_TYPE_NETFILTER
Browse files Browse the repository at this point in the history
Signed-off-by: David Wang <[email protected]>
  • Loading branch information
zq-david-wang committed Sep 8, 2023
1 parent aca10fb commit 40bbc1d
Show file tree
Hide file tree
Showing 6 changed files with 369 additions and 62 deletions.
238 changes: 176 additions & 62 deletions headers/vmlinux/vmlinux_net.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ typedef unsigned int sk_buff_data_t;
typedef unsigned char *sk_buff_data_t;
#endif
*/
struct llist_node {
struct llist_node *next;
};

struct sk_buff {
union {
Expand All @@ -29,6 +32,7 @@ struct sk_buff {
};
struct rb_node rbnode;
struct list_head list;
struct llist_node ll_node;
};
union {
struct sock *sk;
Expand All @@ -45,6 +49,7 @@ struct sk_buff {
void (*destructor)(struct sk_buff *);
};
struct list_head tcp_tsorted_anchor;
long unsigned int _sk_redir;
};
long unsigned int _nfct;
unsigned int len;
Expand All @@ -59,73 +64,152 @@ struct sk_buff {
__u8 peeked: 1;
__u8 head_frag: 1;
__u8 pfmemalloc: 1;
__u8 pp_recycle: 1;
__u8 active_extensions;
__u32 headers_start[0];
__u8 __pkt_type_offset[0];
__u8 pkt_type: 3;
__u8 ignore_df: 1;
__u8 nf_trace: 1;
__u8 ip_summed: 2;
__u8 ooo_okay: 1;
__u8 l4_hash: 1;
__u8 sw_hash: 1;
__u8 wifi_acked_valid: 1;
__u8 wifi_acked: 1;
__u8 no_fcs: 1;
__u8 encapsulation: 1;
__u8 encap_hdr_csum: 1;
__u8 csum_valid: 1;
__u8 __pkt_vlan_present_offset[0];
__u8 vlan_present: 1;
__u8 csum_complete_sw: 1;
__u8 csum_level: 2;
__u8 csum_not_inet: 1;
__u8 dst_pending_confirm: 1;
__u8 ndisc_nodetype: 2;
__u8 ipvs_property: 1;
__u8 inner_protocol_type: 1;
__u8 remcsum_offload: 1;
__u8 offload_fwd_mark: 1;
__u8 offload_l3_fwd_mark: 1;
__u8 tc_skip_classify: 1;
__u8 tc_at_ingress: 1;
__u8 redirected: 1;
__u8 from_ingress: 1;
__u8 decrypted: 1;
__u16 tc_index;
union {
__wsum csum;
struct {
__u16 csum_start;
__u16 csum_offset;
__u8 __pkt_type_offset[0];
__u8 pkt_type: 3;
__u8 ignore_df: 1;
__u8 dst_pending_confirm: 1;
__u8 ip_summed: 2;
__u8 ooo_okay: 1;
__u8 __mono_tc_offset[0];
__u8 mono_delivery_time: 1;
__u8 tc_at_ingress: 1;
__u8 tc_skip_classify: 1;
__u8 remcsum_offload: 1;
__u8 csum_complete_sw: 1;
__u8 csum_level: 2;
__u8 inner_protocol_type: 1;
__u8 l4_hash: 1;
__u8 sw_hash: 1;
__u8 wifi_acked_valid: 1;
__u8 wifi_acked: 1;
__u8 no_fcs: 1;
__u8 encapsulation: 1;
__u8 encap_hdr_csum: 1;
__u8 csum_valid: 1;
__u8 ndisc_nodetype: 2;
__u8 ipvs_property: 1;
__u8 nf_trace: 1;
__u8 redirected: 1;
__u8 from_ingress: 1;
__u8 nf_skip_egress: 1;
__u8 slow_gro: 1;
__u8 csum_not_inet: 1;
__u16 tc_index;
u16 alloc_cpu;
union {
__wsum csum;
struct {
__u16 csum_start;
__u16 csum_offset;
};
};
__u32 priority;
int skb_iif;
__u32 hash;
union {
u32 vlan_all;
struct {
__be16 vlan_proto;
__u16 vlan_tci;
};
};
union {
unsigned int napi_id;
unsigned int sender_cpu;
};
__u32 secmark;
union {
__u32 mark;
__u32 reserved_tailroom;
};
union {
__be16 inner_protocol;
__u8 inner_ipproto;
};
__u16 inner_transport_header;
__u16 inner_network_header;
__u16 inner_mac_header;
__be16 protocol;
__u16 transport_header;
__u16 network_header;
__u16 mac_header;
};
struct {
__u8 __pkt_type_offset[0];
__u8 pkt_type: 3;
__u8 ignore_df: 1;
__u8 dst_pending_confirm: 1;
__u8 ip_summed: 2;
__u8 ooo_okay: 1;
__u8 __mono_tc_offset[0];
__u8 mono_delivery_time: 1;
__u8 tc_at_ingress: 1;
__u8 tc_skip_classify: 1;
__u8 remcsum_offload: 1;
__u8 csum_complete_sw: 1;
__u8 csum_level: 2;
__u8 inner_protocol_type: 1;
__u8 l4_hash: 1;
__u8 sw_hash: 1;
__u8 wifi_acked_valid: 1;
__u8 wifi_acked: 1;
__u8 no_fcs: 1;
__u8 encapsulation: 1;
__u8 encap_hdr_csum: 1;
__u8 csum_valid: 1;
__u8 ndisc_nodetype: 2;
__u8 ipvs_property: 1;
__u8 nf_trace: 1;
__u8 redirected: 1;
__u8 from_ingress: 1;
__u8 nf_skip_egress: 1;
__u8 slow_gro: 1;
__u8 csum_not_inet: 1;
__u16 tc_index;
u16 alloc_cpu;
union {
__wsum csum;
struct {
__u16 csum_start;
__u16 csum_offset;
};
};
__u32 priority;
int skb_iif;
__u32 hash;
union {
u32 vlan_all;
struct {
__be16 vlan_proto;
__u16 vlan_tci;
};
};
union {
unsigned int napi_id;
unsigned int sender_cpu;
};
__u32 secmark;
union {
__u32 mark;
__u32 reserved_tailroom;
};
union {
__be16 inner_protocol;
__u8 inner_ipproto;
};
__u16 inner_transport_header;
__u16 inner_network_header;
__u16 inner_mac_header;
__be16 protocol;
__u16 transport_header;
__u16 network_header;
__u16 mac_header;
} headers;
};
__u32 priority;
int skb_iif;
__u32 hash;
__be16 vlan_proto;
__u16 vlan_tci;
union {
unsigned int napi_id;
unsigned int sender_cpu;
};
__u32 secmark;
union {
__u32 mark;
__u32 reserved_tailroom;
};
union {
__be16 inner_protocol;
__u8 inner_ipproto;
};
__u16 inner_transport_header;
__u16 inner_network_header;
__u16 inner_mac_header;
__be16 protocol;
__u16 transport_header;
__u16 network_header;
__u16 mac_header;
__u32 headers_end[0];
sk_buff_data_t tail;
sk_buff_data_t end;
unsigned char *head;
Expand All @@ -135,4 +219,34 @@ struct sk_buff {
struct skb_ext *extensions;
};



struct iphdr {
__u8 ihl: 4;
__u8 version: 4;
__u8 tos;
__be16 tot_len;
__be16 id;
__be16 frag_off;
__u8 ttl;
__u8 protocol;
__sum16 check;
union {
struct {
__be32 saddr;
__be32 daddr;
};
struct {
__be32 saddr;
__be32 daddr;
} addrs;
};
};

struct bpf_nf_ctx {
const struct nf_hook_state *state;
struct sk_buff *skb;
};


#endif /* __VMLINUX_NET_H__ */
3 changes: 3 additions & 0 deletions headers/vmlinux/vmlinux_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,7 @@ typedef __u64 u64;

typedef s64 ktime_t;

typedef u32 uint32_t;


#endif /* __VMLINUX_TYPES_H__ */
9 changes: 9 additions & 0 deletions netfilter-bpf/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)

USER_TARGETS := netfilter_ip4_blocklist
BPF_TARGETS := netfilter_ip4_blocklist.bpf


LIB_DIR = ../lib

include $(LIB_DIR)/common.mk
20 changes: 20 additions & 0 deletions netfilter-bpf/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Introduction

BPF_PROG_TYPE_NETFILTER was introduced in 6.4, now with a new kernel, a bpf program could attach to netfilter hooks and handles package in a similiar way as iptables/nftables. By now, 6.5.0, there is no bpf kfunc implemented yet for DNAT/SNAT, and the only thing a bpf program can do is to decide whether to DROP the package or not.

* netfilter_ip4_blocklist.c/netfilter_ip4_blocklist.bpf.c

This sample code implements a simple ipv4 blocklist.
The bpf program drops package if destination ip address hits a match in the map of type BPF_MAP_TYPE_LPM_TRIE,
The userspace code would load the bpf program, attach it to netfilter's FORWARD/OUTPUT hook, and then write ip patterns into the bpf map.


# TODO

This sample hard-codes ip address to be blocked, just for demonstration.
It would be better to break the userspace program into two parts:
* init program
Loads bpf program and pin bpf program and map into somewhere under /sys/fs/bpf
* interactive program
add/delete/query ip blocklist via bpf map under /sys/fs/bpf

63 changes: 63 additions & 0 deletions netfilter-bpf/netfilter_ip4_blocklist.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: GPL-2.0

#include "vmlinux_local.h"
#include "linux/bpf.h"
#include <bpf/bpf_helpers.h>


#define NF_DROP 0
#define NF_ACCEPT 1

int bpf_dynptr_from_skb(struct sk_buff *skb,
__u64 flags, struct bpf_dynptr *ptr__uninit) __ksym;
void *bpf_dynptr_slice(const struct bpf_dynptr *ptr,
uint32_t offset, void *buffer, uint32_t buffer__sz) __ksym;


struct ipv4_lpm_key {
__u32 prefixlen;
__u32 data;
};

struct {
__uint(type, BPF_MAP_TYPE_LPM_TRIE);
__type(key, struct ipv4_lpm_key);
__type(value, __u32);
__uint(map_flags, BPF_F_NO_PREALLOC);
__uint(max_entries, 200);
} ipv4_lpm_map SEC(".maps");


SEC("netfilter")
int netfilter_ip4block(struct bpf_nf_ctx *ctx)
{
struct sk_buff *skb = ctx->skb;
struct bpf_dynptr ptr;
struct iphdr *p, iph = {};
struct ipv4_lpm_key key;
__u32 *pvalue;

if (skb->len <= 20 || bpf_dynptr_from_skb(skb, 0, &ptr))
return NF_ACCEPT;
p = bpf_dynptr_slice(&ptr, 0, &iph, sizeof(iph));
if (!p)
return NF_ACCEPT;

/* ip4 only */
if (p->version != 4)
return NF_ACCEPT;

/* search p->daddr in trie */
key.prefixlen = 32;
key.data = p->daddr;
pvalue = bpf_map_lookup_elem(&ipv4_lpm_map, &key);
if (pvalue) {
/* cat /sys/kernel/debug/tracing/trace_pipe */
bpf_printk("rule matched with %d...\n", *pvalue);
return NF_DROP;
}
return NF_ACCEPT;
}

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

Loading

0 comments on commit 40bbc1d

Please sign in to comment.