1
2 #include <linux/netdevice.h>
3 #include <linux/ethtool.h>
4 #if IFNAMSIZ != 16
5 #error "IFNAMSIZ != 16 is not supported"
6 #endif
7 #define MAX_QUEUE_NUM 1024
8
9 /**
10 * This union is use to store name of the specified interface
11 * and read it as two different data types
12 */
13 union name_buf{
14 char name[IFNAMSIZ];
15 struct {
16 u64 hi;
17 u64 lo;
18 }name_int;
19 };
20
21 /* data retrieved in tracepoints */
22 struct queue_data{
23 u64 total_pkt_len;
24 u32 num_pkt;
25 u32 size_64B;
26 u32 size_512B;
27 u32 size_2K;
28 u32 size_16K;
29 u32 size_64K;
30 };
31
32 /* array of length 1 for device name */
33 BPF_ARRAY(name_map, union name_buf, 1);
34 /* table for transmit & receive packets */
35 BPF_HASH(tx_q, u16, struct queue_data, MAX_QUEUE_NUM);
36 BPF_HASH(rx_q, u16, struct queue_data, MAX_QUEUE_NUM);
37
name_filter(struct sk_buff * skb)38 static inline int name_filter(struct sk_buff* skb){
39 /* get device name from skb */
40 union name_buf real_devname;
41 struct net_device *dev;
42 bpf_probe_read(&dev, sizeof(skb->dev), ((char *)skb + offsetof(struct sk_buff, dev)));
43 bpf_probe_read(&real_devname, IFNAMSIZ, dev->name);
44
45 int key=0;
46 union name_buf *leaf = name_map.lookup(&key);
47 if(!leaf){
48 return 0;
49 }
50 if((leaf->name_int).hi != real_devname.name_int.hi || (leaf->name_int).lo != real_devname.name_int.lo){
51 return 0;
52 }
53
54 return 1;
55 }
56
updata_data(struct queue_data * data,u64 len)57 static void updata_data(struct queue_data *data, u64 len){
58 data->total_pkt_len += len;
59 data->num_pkt ++;
60 if(len / 64 == 0){
61 data->size_64B ++;
62 }
63 else if(len / 512 == 0){
64 data->size_512B ++;
65 }
66 else if(len / 2048 == 0){
67 data->size_2K ++;
68 }
69 else if(len / 16384 == 0){
70 data->size_16K ++;
71 }
72 else if(len / 65536 == 0){
73 data->size_64K ++;
74 }
75 }
76
TRACEPOINT_PROBE(net,net_dev_start_xmit)77 TRACEPOINT_PROBE(net, net_dev_start_xmit){
78 /* read device name */
79 struct sk_buff* skb = (struct sk_buff*)args->skbaddr;
80 if(!name_filter(skb)){
81 return 0;
82 }
83
84 /* update table */
85 u16 qid = skb->queue_mapping;
86 struct queue_data newdata;
87 __builtin_memset(&newdata, 0, sizeof(newdata));
88 struct queue_data *data = tx_q.lookup_or_try_init(&qid, &newdata);
89 if(!data){
90 return 0;
91 }
92 updata_data(data, skb->len);
93
94 return 0;
95 }
96
TRACEPOINT_PROBE(net,netif_receive_skb)97 TRACEPOINT_PROBE(net, netif_receive_skb){
98 struct sk_buff skb;
99
100 bpf_probe_read(&skb, sizeof(skb), args->skbaddr);
101 if(!name_filter(&skb)){
102 return 0;
103 }
104
105 /* case 1: if the NIC does not support multi-queue feature, there is only
106 * one queue(qid is always 0).
107 * case 2: if the NIC supports multi-queue feature, there are several queues
108 * with different qid(from 0 to n-1).
109 * The net device driver should mark queue id by API 'skb_record_rx_queue'
110 * for a recieved skb, otherwise it should be a BUG(all of the packets are
111 * reported as queue 0). For example, virtio net driver is fixed for linux:
112 * commit: 133bbb18ab1a2("virtio-net: per-queue RPS config")
113 */
114 u16 qid = 0;
115 if (skb_rx_queue_recorded(&skb))
116 qid = skb_get_rx_queue(&skb);
117
118 struct queue_data newdata;
119 __builtin_memset(&newdata, 0, sizeof(newdata));
120 struct queue_data *data = rx_q.lookup_or_try_init(&qid, &newdata);
121 if(!data){
122 return 0;
123 }
124 updata_data(data, skb.len);
125
126 return 0;
127 }
128