| |
| #include <linux/netdevice.h> |
| #include <linux/ethtool.h> |
| #if IFNAMSIZ != 16 |
| #error "IFNAMSIZ != 16 is not supported" |
| #endif |
| #define MAX_QUEUE_NUM 1024 |
| |
| /** |
| * This union is use to store name of the specified interface |
| * and read it as two different data types |
| */ |
| union name_buf{ |
| char name[IFNAMSIZ]; |
| struct { |
| u64 hi; |
| u64 lo; |
| }name_int; |
| }; |
| |
| /* data retrieved in tracepoints */ |
| struct queue_data{ |
| u64 total_pkt_len; |
| u32 num_pkt; |
| u32 size_64B; |
| u32 size_512B; |
| u32 size_2K; |
| u32 size_16K; |
| u32 size_64K; |
| }; |
| |
| /* array of length 1 for device name */ |
| BPF_ARRAY(name_map, union name_buf, 1); |
| /* table for transmit & receive packets */ |
| BPF_HASH(tx_q, u16, struct queue_data, MAX_QUEUE_NUM); |
| BPF_HASH(rx_q, u16, struct queue_data, MAX_QUEUE_NUM); |
| |
| static inline int name_filter(struct sk_buff* skb){ |
| /* get device name from skb */ |
| union name_buf real_devname; |
| struct net_device *dev; |
| bpf_probe_read(&dev, sizeof(skb->dev), ((char *)skb + offsetof(struct sk_buff, dev))); |
| bpf_probe_read(&real_devname, IFNAMSIZ, dev->name); |
| |
| int key=0; |
| union name_buf *leaf = name_map.lookup(&key); |
| if(!leaf){ |
| return 0; |
| } |
| if((leaf->name_int).hi != real_devname.name_int.hi || (leaf->name_int).lo != real_devname.name_int.lo){ |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| static void updata_data(struct queue_data *data, u64 len){ |
| data->total_pkt_len += len; |
| data->num_pkt ++; |
| if(len / 64 == 0){ |
| data->size_64B ++; |
| } |
| else if(len / 512 == 0){ |
| data->size_512B ++; |
| } |
| else if(len / 2048 == 0){ |
| data->size_2K ++; |
| } |
| else if(len / 16384 == 0){ |
| data->size_16K ++; |
| } |
| else if(len / 65536 == 0){ |
| data->size_64K ++; |
| } |
| } |
| |
| TRACEPOINT_PROBE(net, net_dev_start_xmit){ |
| /* read device name */ |
| struct sk_buff* skb = (struct sk_buff*)args->skbaddr; |
| if(!name_filter(skb)){ |
| return 0; |
| } |
| |
| /* update table */ |
| u16 qid = skb->queue_mapping; |
| struct queue_data newdata; |
| __builtin_memset(&newdata, 0, sizeof(newdata)); |
| struct queue_data *data = tx_q.lookup_or_try_init(&qid, &newdata); |
| if(!data){ |
| return 0; |
| } |
| updata_data(data, skb->len); |
| |
| return 0; |
| } |
| |
| TRACEPOINT_PROBE(net, netif_receive_skb){ |
| struct sk_buff skb; |
| |
| bpf_probe_read(&skb, sizeof(skb), args->skbaddr); |
| if(!name_filter(&skb)){ |
| return 0; |
| } |
| |
| /* case 1: if the NIC does not support multi-queue feature, there is only |
| * one queue(qid is always 0). |
| * case 2: if the NIC supports multi-queue feature, there are several queues |
| * with different qid(from 0 to n-1). |
| * The net device driver should mark queue id by API 'skb_record_rx_queue' |
| * for a recieved skb, otherwise it should be a BUG(all of the packets are |
| * reported as queue 0). For example, virtio net driver is fixed for linux: |
| * commit: 133bbb18ab1a2("virtio-net: per-queue RPS config") |
| */ |
| u16 qid = 0; |
| if (skb_rx_queue_recorded(&skb)) |
| qid = skb_get_rx_queue(&skb); |
| |
| struct queue_data newdata; |
| __builtin_memset(&newdata, 0, sizeof(newdata)); |
| struct queue_data *data = rx_q.lookup_or_try_init(&qid, &newdata); |
| if(!data){ |
| return 0; |
| } |
| updata_data(data, skb.len); |
| |
| return 0; |
| } |