Skip to content

Commit ddc41e2

Browse files
committed
add bcc socket filter support and example
1 parent fb89254 commit ddc41e2

File tree

4 files changed

+183
-0
lines changed

4 files changed

+183
-0
lines changed

bcc/module.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,11 @@ func (bpf *Module) LoadUprobe(name string) (int, error) {
197197
return bpf.Load(name, C.BPF_PROG_TYPE_KPROBE, 0, 0)
198198
}
199199

200+
// LoadSocketFilter loads a program of type BPF_PROG_TYPE_SOCKET_FILTER.
201+
func (bpf *Module) LoadSocketFilter(name string) (int, error) {
202+
return bpf.Load(name, C.BPF_PROG_TYPE_SOCKET_FILTER, 0, 0)
203+
}
204+
200205
// Load a program.
201206
func (bpf *Module) Load(name string, progType int, logLevel, logSize uint) (int, error) {
202207
fd, ok := bpf.funcs[name]
@@ -269,6 +274,15 @@ func (bpf *Module) attachUProbe(evName string, attachType uint32, path string, a
269274
return nil
270275
}
271276

277+
// AttachSocketFilter attach a socket filter to a function
278+
func (bpf *Module) AttachSocketFilter(sockFd, socketFilterFd int) error {
279+
ret, err := C.bpf_attach_socket(C.int(sockFd), C.int(socketFilterFd))
280+
if ret != 0 {
281+
return fmt.Errorf("error attaching BPF socket filter: %v", err)
282+
}
283+
return nil
284+
}
285+
272286
// AttachKprobe attaches a kprobe fd to a function.
273287
func (bpf *Module) AttachKprobe(fnName string, fd int, maxActive int) error {
274288
evName := "p_" + kprobeRegexp.ReplaceAllString(fnName, "_")

examples/bcc/protocol_count/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# usage
2+
3+
1 In current directory (example/bcc/protocol_count)
4+
5+
```bash
6+
go build net_protocol.go
7+
sudo ./net_protocol
8+
```
9+
10+
2 Open another terminal
11+
12+
```bash
13+
ping 127.0.0.1 -c 10
14+
```
15+
16+
3 Result
17+
18+
```
19+
TCP: 0, UDP: 0, ICMP: 0
20+
TCP: 0, UDP: 0, ICMP: 4
21+
TCP: 0, UDP: 0, ICMP: 24
22+
TCP: 0, UDP: 0, ICMP: 40
23+
TCP: 0, UDP: 0, ICMP: 40
24+
TCP: 0, UDP: 0, ICMP: 40
25+
TCP: 4, UDP: 0, ICMP: 40
26+
```
27+
28+
# Misc
29+
30+
Since we run ping for the loop interface, there are 4 packets for one ping including( egress send, ingress receive, egress reply and ingress reply)
31+
32+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#include <linux/if_packet.h>
2+
#include <linux/in.h>
3+
#include <linux/ip.h>
4+
#include <linux/string.h>
5+
#include <linux/tcp.h>
6+
#include <linux/types.h>
7+
#include <linux/udp.h>
8+
9+
#ifndef offsetof
10+
#define offsetof(TYPE, MEMBER) ((size_t) & ((TYPE *)0)->MEMBER)
11+
#endif
12+
13+
#define SEC(NAME) __attribute__((section(NAME), used))
14+
15+
unsigned long long load_byte(void *skb,unsigned long long off) asm("llvm.bpf.load.byte");
16+
BPF_HASH(countmap,u32,u32,32);
17+
int protocol_count(struct __sk_buff *skb) {
18+
19+
int proto = load_byte(skb, ETH_HLEN + offsetof(struct iphdr, protocol));
20+
int one = 1;
21+
22+
int *el = countmap.lookup(&proto);
23+
24+
if (el) {
25+
(*el)++;
26+
} else {
27+
el = &one;
28+
}
29+
countmap.update(&proto,el);
30+
31+
return 0;
32+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"encoding/binary"
6+
"fmt"
7+
"io/ioutil"
8+
"syscall"
9+
"time"
10+
"unsafe"
11+
12+
"github.com/iovisor/gobpf/bcc"
13+
)
14+
15+
const (
16+
PROTOCOL_FUNC = "protocol_count"
17+
PROTOCOL_COUNT = "./net_protocol.c"
18+
LOOP = 1
19+
)
20+
21+
func ReadBPFFile(file string) (string, error) {
22+
content, err := ioutil.ReadFile(file)
23+
if err != nil {
24+
return "", err
25+
}
26+
return string(content), nil
27+
}
28+
29+
func Htons(i uint16) uint16 {
30+
b := make([]byte, 2)
31+
binary.BigEndian.PutUint16(b, i)
32+
return *(*uint16)(unsafe.Pointer(&b[0]))
33+
}
34+
35+
func OpenRawSock(index int) (int, error) {
36+
sock, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, int(Htons(syscall.ETH_P_ALL)))
37+
if err != nil {
38+
return 0, err
39+
}
40+
sll := syscall.SockaddrLinklayer{
41+
Ifindex: index,
42+
Protocol: Htons(syscall.ETH_P_ALL),
43+
}
44+
if err := syscall.Bind(sock, &sll); err != nil {
45+
return 0, err
46+
}
47+
return sock, nil
48+
}
49+
func ProtocolCount(cSource string) {
50+
source, err := ReadBPFFile(cSource)
51+
if err != nil {
52+
fmt.Errorf("read BPF file error: %v", err)
53+
return
54+
}
55+
m := bcc.NewModule(source, []string{})
56+
defer m.Close()
57+
58+
socketFilter, err := m.LoadSocketFilter(PROTOCOL_FUNC)
59+
if err != nil {
60+
fmt.Errorf("socket filter %s not found, err: %v", PROTOCOL_FUNC, err)
61+
return
62+
}
63+
64+
fd, err := OpenRawSock(LOOP)
65+
if err != nil {
66+
fmt.Errorf("unable to open a raw socket: %s", err)
67+
return
68+
}
69+
defer syscall.Close(fd)
70+
71+
if err := m.AttachSocketFilter(fd, socketFilter); err != nil {
72+
fmt.Errorf("failed trying to attach socket filter: %s", err)
73+
return
74+
}
75+
76+
table := bcc.NewTable(m.TableId("countmap"), m)
77+
var tcp, udp, icmp, leafInt, keyInt uint32
78+
hostEndian := bcc.GetHostByteOrder()
79+
for {
80+
iter := table.Iter()
81+
for iter.Next() {
82+
key, leaf := iter.Key(), iter.Leaf()
83+
if err := binary.Read(bytes.NewBuffer(key), hostEndian, &keyInt); err != nil {
84+
continue
85+
}
86+
if err := binary.Read(bytes.NewBuffer(leaf), hostEndian, &leafInt); err != nil {
87+
continue
88+
}
89+
switch keyInt {
90+
case syscall.IPPROTO_TCP:
91+
tcp = leafInt
92+
case syscall.IPPROTO_UDP:
93+
udp = leafInt
94+
case syscall.IPPROTO_ICMP:
95+
icmp = leafInt
96+
}
97+
}
98+
fmt.Printf("TCP: %v, UDP: %v, ICMP: %v\n", tcp, udp, icmp)
99+
time.Sleep(5 * time.Second)
100+
}
101+
}
102+
103+
func main() {
104+
ProtocolCount(PROTOCOL_COUNT)
105+
}

0 commit comments

Comments
 (0)