1#!/usr/bin/env python
2#
3# tc_perf_event.py  Output skb and meta data through perf event
4#
5# Copyright (c) 2016-present, Facebook, Inc.
6# Licensed under the Apache License, Version 2.0 (the "License")
7
8from bcc import BPF
9import ctypes as ct
10import pyroute2
11import socket
12
13bpf_txt = """
14#include <uapi/linux/if_ether.h>
15#include <uapi/linux/in6.h>
16#include <uapi/linux/ipv6.h>
17#include <uapi/linux/pkt_cls.h>
18#include <uapi/linux/bpf.h>
19
20BPF_PERF_OUTPUT(skb_events);
21
22struct eth_hdr {
23	unsigned char   h_dest[ETH_ALEN];
24	unsigned char   h_source[ETH_ALEN];
25	unsigned short  h_proto;
26};
27
28int handle_egress(struct __sk_buff *skb)
29{
30	void *data = (void *)(long)skb->data;
31	void *data_end = (void *)(long)skb->data_end;
32	struct eth_hdr *eth = data;
33	struct ipv6hdr *ip6h = data + sizeof(*eth);
34	u32 magic = 0xfaceb00c;
35
36	/* single length check */
37	if (data + sizeof(*eth) + sizeof(*ip6h) > data_end)
38		return TC_ACT_OK;
39
40	if (eth->h_proto == htons(ETH_P_IPV6) &&
41	    ip6h->nexthdr == IPPROTO_ICMPV6)
42	        skb_events.perf_submit_skb(skb, skb->len, &magic, sizeof(magic));
43
44	return TC_ACT_OK;
45}"""
46
47def print_skb_event(cpu, data, size):
48    class SkbEvent(ct.Structure):
49        _fields_ =  [ ("magic", ct.c_uint32),
50                      ("raw", ct.c_ubyte * (size - ct.sizeof(ct.c_uint32))) ]
51
52    skb_event = ct.cast(data, ct.POINTER(SkbEvent)).contents
53    icmp_type = int(skb_event.raw[54])
54
55    # Only print for echo request
56    if icmp_type == 128:
57        src_ip = bytes(bytearray(skb_event.raw[22:38]))
58        dst_ip = bytes(bytearray(skb_event.raw[38:54]))
59        print("%-3s %-32s %-12s 0x%08x" %
60              (cpu, socket.inet_ntop(socket.AF_INET6, src_ip),
61               socket.inet_ntop(socket.AF_INET6, dst_ip),
62               skb_event.magic))
63
64try:
65    b = BPF(text=bpf_txt)
66    fn = b.load_func("handle_egress", BPF.SCHED_CLS)
67
68    ipr = pyroute2.IPRoute()
69    ipr.link("add", ifname="me", kind="veth", peer="you")
70    me = ipr.link_lookup(ifname="me")[0]
71    you = ipr.link_lookup(ifname="you")[0]
72    for idx in (me, you):
73        ipr.link('set', index=idx, state='up')
74
75    ipr.tc("add", "clsact", me)
76    ipr.tc("add-filter", "bpf", me, ":1", fd=fn.fd, name=fn.name,
77           parent="ffff:fff3", classid=1, direct_action=True)
78
79    b["skb_events"].open_perf_buffer(print_skb_event)
80    print('Try: "ping6 ff02::1%me"\n')
81    print("%-3s %-32s %-12s %-10s" % ("CPU", "SRC IP", "DST IP", "Magic"))
82    while True:
83        b.perf_buffer_poll()
84finally:
85    if "me" in locals(): ipr.link("del", index=me)
86