Skip to content

Commit

Permalink
bcc-python: support attach_func() and detach_func() (iovisor#3479)
Browse files Browse the repository at this point in the history
 - support attach_func() and detach_func().
 - add an sockmap issue to demonstrate using these two functions.
  • Loading branch information
chenyuezhou committed Jun 14, 2021
1 parent ce7e523 commit 5934161
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 1 deletion.
40 changes: 39 additions & 1 deletion docs/reference_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ This guide is incomplete. If something feels missing, check the bcc and kernel s
- [7. attach_raw_tracepoint()](#7-attach_raw_tracepoint)
- [8. attach_raw_socket()](#8-attach_raw_socket)
- [9. attach_xdp()](#9-attach_xdp)
- [10. attach_func()](#10-attach_func)
- [11. detach_func()](#11-detach_func)
- [Debug Output](#debug-output)
- [1. trace_print()](#1-trace_print)
- [2. trace_fields()](#2-trace_fields)
Expand Down Expand Up @@ -1777,7 +1779,7 @@ Examples in situ:

Syntax: ```BPF.attach_raw_socket(fn, dev)```

Attache a BPF function to the specified network interface.
Attaches a BPF function to the specified network interface.

The ```fn``` must be the type of ```BPF.function``` and the bpf_prog type needs to be ```BPF_PROG_TYPE_SOCKET_FILTER``` (```fn=BPF.load_func(func_name, BPF.SOCKET_FILTER)```)

Expand Down Expand Up @@ -1847,6 +1849,42 @@ Examples in situ:
[search /examples](https://github.com/iovisor/bcc/search?q=attach_xdp+path%3Aexamples+language%3Apython&type=Code),
[search /tools](https://github.com/iovisor/bcc/search?q=attach_xdp+path%3Atools+language%3Apython&type=Code)

### 10. attach_func()

Syntax: ```BPF.attach_func(fn, attachable_fd, attach_type [, flags])```

Attaches a BPF function of the specified type to a particular ```attachable_fd```. if the ```attach_type``` is ```BPF_FLOW_DISSECTOR```, the function is expected to attach to current net namespace and ```attachable_fd``` must be 0.

For example:

```Python
b.attach_func(fn, cgroup_fd, b.CGROUP_SOCK_OPS)
b.attach_func(fn, map_fd, b.SK_MSG_VERDICT)
```

Note. When attached to "global" hooks (xdp, tc, lwt, cgroup). If the "BPF function" is no longer needed after the program terminates, be sure to call `detach_func` when the program exits.

Examples in situ:

[search /examples](https://github.com/iovisor/bcc/search?q=attach_func+path%3Aexamples+language%3Apython&type=Code),

### 11. detach_func()

Syntax: ```BPF.detach_func(fn, attachable_fd, attach_type)```

Detaches a BPF function of the specified type.

For example:

```Python
b.detach_func(fn, cgroup_fd, b.CGROUP_SOCK_OPS)
b.detach_func(fn, map_fd, b.SK_MSG_VERDICT)
```

Examples in situ:

[search /examples](https://github.com/iovisor/bcc/search?q=detach_func+path%3Aexamples+language%3Apython&type=Code),

## Debug Output

### 1. trace_print()
Expand Down
130 changes: 130 additions & 0 deletions examples/networking/sockmap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
#!/usr/bin/python
# @lint-avoid-python-3-compatibility-imports
#
# Copyright (c) 2021 Chenyue Zhou

from __future__ import print_function
import os
import sys
import time
import atexit
import argparse

from bcc import BPF, lib


examples = """examples:
./sockmap.py -c /root/cgroup # attach to /root/cgroup
"""
parser = argparse.ArgumentParser(
description="pipe data across multiple sockets",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=examples)
parser.add_argument("-c", "--cgroup", required=True,
help="Specify the cgroup address. Note. must be cgroup2")

bpf_text = '''
#include <net/sock.h>
#define MAX_SOCK_OPS_MAP_ENTRIES 65535
struct sock_key {
u32 remote_ip4;
u32 local_ip4;
u32 remote_port;
u32 local_port;
u32 family;
};
BPF_SOCKHASH(sock_hash, struct sock_key, MAX_SOCK_OPS_MAP_ENTRIES);
static __always_inline void bpf_sock_ops_ipv4(struct bpf_sock_ops *skops) {
struct sock_key skk = {
.remote_ip4 = skops->remote_ip4,
.local_ip4 = skops->local_ip4,
.local_port = skops->local_port,
.remote_port = bpf_ntohl(skops->remote_port),
.family = skops->family,
};
int ret;
bpf_trace_printk("remote-port: %d, local-port: %d\\n", skk.remote_port,
skk.local_port);
ret = sock_hash.sock_hash_update(skops, &skk, BPF_NOEXIST);
if (ret) {
bpf_trace_printk("bpf_sock_hash_update() failed. %d\\n", -ret);
return;
}
bpf_trace_printk("Sockhash op: %d, port %d --> %d\\n", skops->op,
skk.local_port, skk.remote_port);
}
int bpf_sockhash(struct bpf_sock_ops *skops) {
u32 op = skops->op;
/* ipv4 only */
if (skops->family != AF_INET)
return 0;
switch (op) {
case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
bpf_sock_ops_ipv4(skops);
break;
default:
break;
}
return 0;
}
int bpf_redir(struct sk_msg_md *msg) {
if (msg->family != AF_INET)
return SK_PASS;
if (msg->remote_ip4 != msg->local_ip4)
return SK_PASS;
struct sock_key skk = {
.remote_ip4 = msg->local_ip4,
.local_ip4 = msg->remote_ip4,
.local_port = bpf_ntohl(msg->remote_port),
.remote_port = msg->local_port,
.family = msg->family,
};
int ret = 0;
ret = sock_hash.msg_redirect_hash(msg, &skk, BPF_F_INGRESS);
bpf_trace_printk("try redirect port %d --> %d\\n", msg->local_port,
bpf_ntohl(msg->remote_port));
if (ret != SK_PASS)
bpf_trace_printk("redirect port %d --> %d failed\\n", msg->local_port,
bpf_ntohl(msg->remote_port));
return ret;
}
'''
args = parser.parse_args()
bpf = BPF(text=bpf_text)
func_sock_ops = bpf.load_func("bpf_sockhash", bpf.SOCK_OPS)
func_sock_redir = bpf.load_func("bpf_redir", bpf.SK_MSG)
# raise if error
fd = os.open(args.cgroup, os.O_RDONLY)
map_fd = lib.bpf_table_fd(bpf.module, b"sock_hash")
bpf.attach_func(func_sock_ops, fd, bpf.CGROUP_SOCK_OPS)
bpf.attach_func(func_sock_redir, map_fd, bpf.SK_MSG_VERDICT)

def detach_all():
bpf.detach_func(func_sock_ops, fd, bpf.CGROUP_SOCK_OPS)
bpf.detach_func(func_sock_redir, map_fd, bpf.SK_MSG_VERDICT)
print("Detaching...")

atexit.register(detach_all)

while True:
try:
bpf.trace_print()
sleep(1)
except KeyboardInterrupt:
sys.exit(0)
57 changes: 57 additions & 0 deletions src/python/bcc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,45 @@ class BPF(object):
XDP_FLAGS_REPLACE = (1 << 4)

# from bpf_attach_type uapi/linux/bpf.h
CGROUP_INET_INGRESS = 0
CGROUP_INET_EGRESS = 1
CGROUP_INET_SOCK_CREATE = 2
CGROUP_SOCK_OPS = 3
SK_SKB_STREAM_PARSER = 4
SK_SKB_STREAM_VERDICT = 5
CGROUP_DEVICE = 6
SK_MSG_VERDICT = 7
CGROUP_INET4_BIND = 8
CGROUP_INET6_BIND = 9
CGROUP_INET4_CONNECT = 10
CGROUP_INET6_CONNECT = 11
CGROUP_INET4_POST_BIND = 12
CGROUP_INET6_POST_BIND = 13
CGROUP_UDP4_SENDMSG = 14
CGROUP_UDP6_SENDMSG = 15
LIRC_MODE2 = 16
FLOW_DISSECTOR = 17
CGROUP_SYSCTL = 18
CGROUP_UDP4_RECVMSG = 19
CGROUP_UDP6_RECVMSG = 20
CGROUP_GETSOCKOPT = 21
CGROUP_SETSOCKOPT = 22
TRACE_RAW_TP = 23
TRACE_FENTRY = 24
TRACE_FEXIT = 25
MODIFY_RETURN = 26
LSM_MAC = 27
TRACE_ITER = 28
CGROUP_INET4_GETPEERNAME = 29
CGROUP_INET6_GETPEERNAME = 30
CGROUP_INET4_GETSOCKNAME = 31
CGROUP_INET6_GETSOCKNAME = 32
XDP_DEVMAP = 33
CGROUP_INET_SOCK_RELEASE = 34
XDP_CPUMAP = 35
SK_LOOKUP = 36
XDP = 37
SK_SKB_VERDICT = 38

_probe_repl = re.compile(b"[^a-zA-Z0-9_]")
_sym_caches = {}
Expand Down Expand Up @@ -544,6 +581,26 @@ def __delitem__(self, key):
def __iter__(self):
return self.tables.__iter__()

@staticmethod
def attach_func(fn, attachable_fd, attach_type, flags=0):
if not isinstance(fn, BPF.Function):
raise Exception("arg 1 must be of type BPF.Function")

res = lib.bpf_prog_attach(fn.fd, attachable_fd, attach_type, flags)
if res < 0:
raise Exception("Failed to attach BPF function with attach_type "\
"{0}: {1}".format(attach_type, os.strerror(-res)))

@staticmethod
def detach_func(fn, attachable_fd, attach_type):
if not isinstance(fn, BPF.Function):
raise Exception("arg 1 must be of type BPF.Function")

res = lib.bpf_prog_detach2(fn.fd, attachable_fd, attach_type)
if res < 0:
raise Exception("Failed to detach BPF function with attach_type "\
"{0}: {1}".format(attach_type, os.strerror(-res)))

@staticmethod
def attach_raw_socket(fn, dev):
dev = _assert_is_bytes(dev)
Expand Down
4 changes: 4 additions & 0 deletions src/python/bcc/libbcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@
lib.bpf_attach_kfunc.argtypes = [ct.c_int]
lib.bpf_attach_lsm.restype = ct.c_int
lib.bpf_attach_lsm.argtypes = [ct.c_int]
lib.bpf_prog_attach.restype = ct.c_int
lib.bpf_prog_attach.argtype = [ct.c_int, ct.c_int, ct.c_int, ct.c_uint]
lib.bpf_prog_detach2.restype = ct.c_int
lib.bpf_prog_detach2.argtype = [ct.c_int, ct.c_int, ct.c_int]
lib.bpf_has_kernel_btf.restype = ct.c_bool
lib.bpf_has_kernel_btf.argtypes = None
lib.bpf_open_perf_buffer.restype = ct.c_void_p
Expand Down

0 comments on commit 5934161

Please sign in to comment.