Linux pktgen发包内核模块与pg_ctrl_show配置 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_workerstatic 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 parametersreturn 0;}pktgen通过dev_direct_xmit直接发送不经过qdisc排队最大限度降低发送延迟实现线速发包。clone_skb参数控制是否复用skb数据结构设为0表示每次分配新skb0表示复用指定次数以减少内存分配开销。