Linux pktgen发包内核模块与pg_ctrl_show配置
Pktgen位于net/core/pktgen.c,是内核内置的高速发包模块,绕过标准协议栈直接构造并发送原始报文。每个线程(kthread)管理多个device实例,通过/proc/net/pktgen/下的控制文件进行配置。
入口函数pktgen_write处理用户写入/proc/net/pktgen/的配置命令:
static ssize_t pktgen_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct pktgen_dev *pktgen = PDE_DATA(file_inode(file));
char *data;
int len = count;
char *command;
char *val;
int err = 0;
data = memdup_user_nul(buf, count);
if (IS_ERR(data))
return PTR_ERR(data);
command = strstrip(strsep(&data, "\n"));
while (command) {
val = strchr(command, ' ');
if (val)
*val++ = '\0';
err = pktgen_add_device(pktgen, command, val);
if (err)
goto out;
command = strstrip(strsep(&data, "\n"));
}
out:
kfree(data);
return err ? : count;
}
pg_ctrl_show是/proc/net/pktgen/pgctrl的show回调,用于显示全局配置或触发控制命令:
static int pgctrl_show(struct seq_file *seq, void *v)
{
struct pktgen_net *pn = seq->private;
struct pktgen_thread *t;
mutex_lock(&pktgen_lock);
list_for_each_entry(t, &pn->pktgen_threads, th_list) {
seq_printf(seq, "Thread: %s\n", t->tsk->comm);
// per-thread stats
}
mutex_unlock(&pktgen_lock);
return 0;
}
pgctrl_write解析用户写入的控制命令(start/stop/reset等):
static ssize_t pgctrl_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
struct pktgen_net *pn = net_generic(current->nsproxy->net_ns,
pktgen_net_id);
char data[128];
int err = 0;
if (count >= sizeof(data))
return -EINVAL;
if (copy_from_user(data, buf, count))
return -EFAULT;
data[count] = '\0';
mutex_lock(&pktgen_lock);
if (strcmp(data, "start") == 0) {
err = pktgen_start(pn);
} else if (strcmp(data, "stop") == 0) {
pktgen_stop(pn);
} else if (strcmp(data, "reset") == 0) {
pktgen_reset_all(pn);
} else {
err = -EINVAL;
}
mutex_unlock(&pktgen_lock);
return err ? : count;
}
pktgen_start启动所有线程的发包循环。每个线程的Worker函数为pktgen_thread_worker:
static int pktgen_thread_worker(void *data)
{
struct pktgen_thread *t = data;
struct pktgen_dev *pktgen;
DEFINE_WAIT(wait);
set_freezable();
for (;;) {
prepare_to_wait(&t->wq, &wait, TASK_INTERRUPTIBLE);
if (unlikely(kthread_should_stop()))
break;
if (t->control == STOP)
schedule_timeout_uninterruptible(HZ);
finish_wait(&t->wq, &wait);
mutex_lock(&pktgen_lock);
pktgen_stop_device(t);
if (t->control == RUNNING) {
list_for_each_entry(pktgen, &t->if_list, list) {
if (pktgen->running) {
pktgen->next_tx = jiffies;
pktgen_xmit(pktgen);
}
}
}
mutex_unlock(&pktgen_lock);
inc_rt_sched();
}
return 0;
}
pktgen_xmit是实际报文构造和发送的核心函数:
static void pktgen_xmit(struct pktgen_dev *pktgen)
{
struct sk_buff *skb;
struct net_device *odev;
struct netdev_queue *txq;
ktime_t started;
int ret;
if (pktgen->started_at == 0)
pktgen->started_at = ktime_get_ns();
odev = pktgen->odev;
if (!odev || !netif_running(odev))
return;
if (time_before(jiffies, pktgen->next_tx))
return;
skb = pktgen_alloc_skb(odev, pktgen);
if (!skb) {
pktgen->errors++;
return;
}
pktgen_finalize_skb(pktgen, skb, 0);
pktgen->last_ok = 1;
skb->dev = odev;
skb->priority = pktgen->priority;
skb->protocol = eth_type_trans(skb, odev);
started = ktime_get();
ret = dev_direct_xmit(skb, pktgen->queue_map);
if (ret == NETDEV_TX_OK) {
pktgen->packets++;
pktgen->bytes += skb->len;
pktgen->sofar++;
} else {
pktgen->errors++;
pktgen->last_ok = 0;
}
pktgen->next_tx = jiffies + usecs_to_jiffies(pktgen->delay);
if (pktgen->sofar >= pktgen->count && pktgen->count)
pktgen->running = 0;
}
pktgen_finalize_skb根据配置构造完整的以太网/IP/UDP报文:
static void pktgen_finalize_skb(struct pktgen_dev *pktgen,
struct sk_buff *skb, int datalen)
{
struct ethhdr *eth;
struct iphdr *iph;
struct udphdr *udph;
eth = eth_hdr(skb);
eth->h_proto = htons(ETH_P_IP);
memcpy(eth->h_source, pktgen->src_mac, ETH_ALEN);
memcpy(eth->h_dest, pktgen->dst_mac, ETH_ALEN);
skb_reset_network_header(skb);
iph = ip_hdr(skb);
iph->version = 4;
iph->ihl = 5;
iph->tos = pktgen->tos;
iph->tot_len = htons(sizeof(struct iphdr) +
sizeof(struct udphdr) + datalen);
iph->id = htons(pktgen->cur_ip_id++);
iph->frag_off = htons(0x4000);
iph->ttl = pktgen->ttl;
iph->protocol = IPPROTO_UDP;
iph->saddr = pktgen->cur_saddr;
iph->daddr = pktgen->cur_daddr;
skb_reset_transport_header(skb);
udph = udp_hdr(skb);
udph->source = htons(pktgen->cur_sport);
udph->dest = htons(pktgen->cur_dport);
udph->len = htons(sizeof(struct udphdr) + datalen);
udph->check = 0;
if (pktgen->flags & F_IPSRC_RND) {
pktgen->cur_saddr = random32();
iph->saddr = pktgen->cur_saddr;
}
if (pktgen->flags & F_IPDST_RND) {
pktgen->cur_daddr = random32();
iph->daddr = pktgen->cur_daddr;
}
}
配置参数通过procfs以键值对写入,如clone_skb、pkt_size、min_pkt_size、max_pkt_size、dst_ip、src_ip等。pktgen_add_device函数解析这些参数:
static int pktgen_add_device(struct pktgen_dev *pktgen,
char *command, char *val)
{
if (strcmp(command, "clone_skb") == 0) {
pktgen->clone_skb = simple_strtol(val, NULL, 10);
} else if (strcmp(command, "pkt_size") == 0) {
pktgen->min_pkt_size = simple_strtol(val, NULL, 10);
pktgen->max_pkt_size = pktgen->min_pkt_size;
} else if (strcmp(command, "min_pkt_size") == 0) {
pktgen->min_pkt_size = simple_strtol(val, NULL, 10);
} else if (strcmp(command, "max_pkt_size") == 0) {
pktgen->max_pkt_size = simple_strtol(val, NULL, 10);
} else if (strcmp(command, "src_mac") == 0) {
mac_pton(val, pktgen->src_mac);
} else if (strcmp(command, "dst_mac") == 0) {
mac_pton(val, pktgen->dst_mac);
} else if (strcmp(command, "dst_ip") == 0) {
pktgen->dst_min = in_aton(val);
pktgen->dst_max = pktgen->dst_min;
}
// ... more parameters
return 0;
}
pktgen通过dev_direct_xmit直接发送,不经过qdisc排队,最大限度降低发送延迟,实现线速发包。clone_skb参数控制是否复用skb数据结构,设为0表示每次分配新skb,>0表示复用指定次数以减少内存分配开销。
Linux pktgen发包内核模块与pg_ctrl_show配置
张小明
前端开发工程师
DHT11 温湿度 LCD1602 显示与报警 FPGA 设计 Verilog Vivado
名称:DHT11 温湿度 LCD1602 显示与报警 FPGA 设计 Verilog Vivado软件:Vivado语言:Verilog功能介绍本设计实现了基于 DHT11 的温湿度采集、数据处理和 LCD1602 字符液晶显示功能。FPGA 读取 DHT11 输出的温度、湿度数据后,将数值转…
终极免费音乐解锁指南:Unlock Music完整使用教程
终极免费音乐解锁指南:Unlock Music完整使用教程 【免费下载链接】unlock-music 在浏览器中解锁加密的音乐文件。原仓库: 1. https://github.com/unlock-music/unlock-music ;2. https://git.unlock-music.dev/um/web 项目地址: https://gi…
LIO-SAM适配KITTI数据集,你的点云格式对了吗?详解XYZIRT与时间同步
LIO-SAM适配KITTI数据集:点云格式与时间同步的深度解析当开发者尝试将LIO-SAM这类先进的激光雷达-惯性里程计系统应用于KITTI数据集时,往往会遇到一个看似简单却影响深远的问题:为什么算法对点云格式如此挑剔?XYZIRT与传统XYZI的区…
IR-UWB vs FMCW雷达:在智能家居与养老监护中,哪种技术方案更靠谱?
IR-UWB与FMCW雷达技术深度解析:智能健康监护场景下的选型指南清晨六点,李工程师的智能手环再次发出警报——独居母亲的夜间心率出现异常波动。这是本月第三次误报,而墙另一侧的毫米波雷达却显示生命体征完全正常。这种技术间的性能差异&#…
新手避坑指南:别再搞混Pixhawk、PX4和APM了!手把手教你选对硬件和固件组合
开源飞控实战指南:Pixhawk硬件与PX4/ArduPilot固件的黄金组合法则当你第一次踏入无人机DIY的世界,面对淘宝上琳琅满目的"Pixhawk飞控"和论坛里频繁出现的"PX4"、"ArduPilot"等术语,是否感到一头雾水࿱…
003、ChatGPT 会员体系与 CodeX 权限:Plus、Pro、Team 的限额与功能对比
003、ChatGPT 会员体系与 CodeX 权限:Plus、Pro、Team 的限额与功能对比上周五凌晨两点,我正用 CodeX 调试一个微服务间的 gRPC 调用超时问题。代码写到一半,突然弹出一条红色提示:“You’ve reached the GPT-4 limit for your pl…