#include #include #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; }