kretbrobe:
Linux协议栈处理发送流程时,在发送驱动函数前会通过__netdev_pick_tx选择一个发送队列,当选择好一个发送队列后,协议栈会将其设置到sk->sk_tx_queue_mapping,这样后续的发送流程就不需要再去选择。
static u16 __netdev_pick_tx(struct net_device *dev, struct sk_buff *skb)
{
struct sock *sk = skb->sk;
int queue_index = sk_tx_queue_get(sk);
//skb->ooo_okay对于udp没有意义,针对tcp,当tcp层的发送端检测到所有
//发送出去的skb都已经被ack时,就会对ooo_okay置1,这时候这里就允许重新
//选择一个不一样的队列了,否则会沿用上一次选择的队列
if (queue_index < 0 || skb->ooo_okay ||
queue_index >= dev->real_num_tx_queues) {
int new_index = get_xps_queue(dev, skb);
if (new_index < 0)
new_index = skb_tx_hash(dev, skb);
if (queue_index != new_index && sk &&
sk_fullsock(sk) &&
rcu_access_pointer(sk->sk_dst_cache))
sk_tx_queue_set(sk, new_index);
queue_index = new_index;
}
return queue_index;
}
下面主要是使用retkprobe探测tcp流选择的队列信息:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/ktime.h>
#include <linux/limits.h>
#include <linux/sched.h>
#include <uapi/linux/tcp.h>
#include <uapi/linux/if_ether.h>
#include <uapi/linux/if_packet.h>
#include <linux/ip.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <net/sock.h>
//需要探测的函数名称
static char func_name[NAME_MAX] = "__netdev_pick_tx";
struct my_data {
ktime_t entry_stamp;
};
static int count=0;
static long int queue_counts[32] = {};
static __be32 cpu_src[32] = {};
static __be32 cpu_dst[32] = {};
static int entry_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
//regs->si为__netdev_pick_tx的第二个参数(skb)
struct sk_buff *skb = regs->si;
const struct iphdr *iph;
iph = ip_hdr(skb);
//按cpu_id为索引,将ip的源、宿信息保存到对应的数组里
int cpu_id = raw_smp_processor_id();
cpu_src[cpu_id] = iph->saddr;
cpu_dst[cpu_id] = iph->daddr;
int ooo_okay = skb->ooo_okay;
struct sock *sk = skb->sk;
int queue_index = sk_tx_queue_get(sk);
if (cpu_src[cpu_id] == 2196511856 && cpu_dst[cpu_id] == 2347506800) {
net_info_ratelimited("entry handle ooo_okay: %d, queue_index:%d, sk: %p \n", ooo_okay,queue_index, sk);
}
//net_info_ratelimited("netdev_cap_txqueue %pI4 -> %pI4 %u ~ %u cpu: %d %u ~ %u \n", &iph->saddr,&iph->daddr, iph->saddr, iph->daddr, cpu_id, cpu_src[cpu_id], cpu_dst[cpu_id]);
return 0;
}
static int ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
//获取函数的返回值
int queue_index = regs_return_value(regs);
//将当前选择的队列计数加1
int cpu_id = raw_smp_processor_id();
if (cpu_src[cpu_id] == 2196511856 && cpu_dst[cpu_id] == 2347506800) {
queue_counts[queue_index]++;
}
net_info_ratelimited("per cpu packets %ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld \n %ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld \n %ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld,\n %ld,%ld,%ld,%ld,%ld,%ld,%ld,%ld \n",
queue_counts[0],queue_counts[1],queue_counts[2],queue_counts[3],queue_counts[4],queue_counts[5],queue_counts[6],queue_counts[7],
queue_counts[8],queue_counts[9],queue_counts[10],queue_counts[11],queue_counts[12],queue_counts[13],queue_counts[14],queue_counts[15],
queue_counts[16],queue_counts[17],queue_counts[18],queue_counts[19],queue_counts[20],queue_counts[21],queue_counts[22],queue_counts[23],
queue_counts[24],queue_counts[25],queue_counts[26],queue_counts[27],queue_counts[28],queue_counts[29],queue_counts[30],queue_counts[31]);
}
static struct kretprobe my_kretprobe = {
//handler退出函数时执行
.handler = ret_handler,
//entry_handle进入函数时执行
.entry_handler = entry_handler,
.data_size = sizeof(struct my_data),
/* Probe up to 20 instances concurrently. */
.maxactive = 20,
};
static int __init kretprobe_init(void)
{
int ret;
//注册retkprobe
my_kretprobe.kp.symbol_name = func_name;
ret = register_kretprobe(&my_kretprobe);
if (ret < 0) {
printk(KERN_INFO "register_kretprobe failed, returned %d\n",
ret);
return -1;
}
printk(KERN_INFO "Planted return probe at %s: %p\n",
my_kretprobe.kp.symbol_name, my_kretprobe.kp.addr);
return 0;
}
static void __exit kretprobe_exit(void)
{
unregister_kretprobe(&my_kretprobe);
printk(KERN_INFO "kretprobe at %p unregistered\n",
my_kretprobe.kp.addr);
}
module_init(kretprobe_init)
module_exit(kretprobe_exit)
MODULE_LICENSE("GPL");
kprobe
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/ktime.h>
#include <linux/limits.h>
#include <linux/sched.h>
#include <uapi/linux/tcp.h>
#include <uapi/linux/if_ether.h>
#include <uapi/linux/if_packet.h>
#include <linux/ip.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <net/sock.h>
static void handler_post(struct kprobe *p, struct pt_regs *regs,
unsigned long flags)
{
struct sk_buff *skb = (struct sk_buff *)(regs->di);
struct ethhdr *eth = eth_hdr(skb);
struct iphdr *iph = ip_hdr(skb);
if (skb->pkt_type == PACKET_OTHERHOST) {
printk("receive other host skb src_ip: %pI4, dst_ip: %pI4, skb->dest_mac: %pM, dev->mac: %pM \n", &iph->saddr, &iph->daddr, eth->h_dest, skb->dev->dev_addr);
}
}
static struct kprobe my_kprobe = {
.symbol_name = "ip_rcv",
.post_handler = handler_post
};
static int __init kprobe_init(void)
{
int ret;
ret = register_kprobe(&my_kprobe);
if (ret < 0) {
printk(KERN_INFO "register_kprobe failed, returned %d\n",
ret);
return -1;
}
printk(KERN_INFO "Planted return probe at %s \n",
my_kprobe.symbol_name);
return 0;
}
static void __exit kprobe_exit(void)
{
unregister_kprobe(&my_kprobe);
printk(KERN_INFO "kprobe at %s unregistered\n",
my_kprobe.symbol_name);
}
module_init(kprobe_init)
module_exit(kprobe_exit)
MODULE_LICENSE("GPL");