当前位置:网站首页>tcpdump抓包实现过程
tcpdump抓包实现过程
2022-06-24 07:07:00 【为了维护世界和平_】
目录
tcpdump抓包源码分析
应用层
协议族AF_PACKET
socket(AF_PACKET, SOCK_RAW, ETH_P_ALL)协议族和地址族关系:每一种协议族都有对应的地址族。IPV4的协议族PF_INET,地址族为AF_INET,一一对应,值完全一样,经常混用。
socket内核实现
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
{
return __sys_socket(family, type, protocol);
}
int __sys_socket(int family, int type, int protocol)
{
retval = sock_create(family, type, protocol, &sock);
}
int __sock_create(struct net *net, int family, int type, int protocol,
struct socket **res, int kern)
{
pf = rcu_dereference(net_families[family]);
err = pf->create(net, sock, protocol, kern);
}从net_families中获取指定协议,并调用create方法创建
static const struct net_proto_family packet_family_ops = {
.family = PF_PACKET,
.create = packet_create,
.owner = THIS_MODULE,
};
static int __init packet_init(void)
{
rc = sock_register(&packet_family_ops);
}
//注册packet_family_ops 到net_families中
int sock_register(const struct net_proto_family *ops)
{
rcu_assign_pointer(net_families[ops->family], ops);
}
//pf->create就是packet_create
static int packet_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
//创建时的状态
sock->state = SS_UNCONNECTED;
sk = sk_alloc(net, PF_PACKET, GFP_KERNEL, &packet_proto, kern);
sock->ops = &packet_ops;
po = pkt_sk(sk);
//拥塞控制
init_completion(&po->skb_completion);
sk->sk_family = PF_PACKET;
po->xmit = dev_queue_xmit;
//fun上注册回调函数为 packet_rcv
po->prot_hook.func = packet_rcv;
if (proto) {
po->prot_hook.type = proto;
__register_prot_hook(sk);
}
}
static void __register_prot_hook(struct sock *sk)
{
struct packet_sock *po = pkt_sk(sk);
dev_add_pack(&po->prot_hook);//注册
}
void dev_add_pack(struct packet_type *pt)
{
struct list_head *head = ptype_head(pt);
list_add_rcu(&pt->list, head);
}
//ptype_head
static inline struct list_head *ptype_head(const struct packet_type *pt)
{
if (pt->type == htons(ETH_P_ALL))
return pt->dev ? &pt->dev->ptype_all : &ptype_all;
else
return pt->dev ? &pt->dev->ptype_specific :
&ptype_base[ntohs(pt->type) & PTYPE_HASH_MASK];
}dev_add_pack 其实最后是把 hook 函数添加到了 ptype_all 里了,代码如下。
内核根据ptype_all找抓包点(重要)
static int __netif_receive_skb_core(struct sk_buff *skb, bool pfmemalloc)
{
......
//遍历 ptype_all (tcpdump 在这里挂了虚拟协议)
list_for_each_entry_rcu(ptype, &ptype_all, list) {
if (pt_prev)
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = ptype;
}
}
static inline int deliver_skb(struct sk_buff *skb,
struct packet_type *pt_prev,
struct net_device *orig_dev)
{
//这个回调函数就是注册的packet_rcv
return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
}
//将skb添加到sk_receive_queue中
static int packet_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *pt, struct net_device *orig_dev)
{
__skb_queue_tail(&sk->sk_receive_queue, skb);
}
可见 packet_rcv 把收到的 skb 放到了当前 packet socket 的接收队列里了。调用 recvfrom 的时候就可以获取到所抓到的包
过滤包抓包点netfilter(重要)
网络接收不经过netfilter
网络发包经过netfilter
发包 IP层各种 netfilter 规则的过滤
int __ip_local_out(struct sk_buff *skb)
{
......
return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT, skb, NULL,
skb_dst(skb)->dev, dst_output);
}
struct sk_buff *dev_hard_start_xmit(struct sk_buff *first, struct net_device *dev,
struct netdev_queue *txq, int *ret)
{
while (skb) {
rc = xmit_one(skb, dev, txq, next != NULL);
}
}
//xmit_one->dev_queue_xmit_nit
void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)
{
list_for_each_entry_rcu(ptype, ptype_list, list) {
if (ptype->ignore_outgoing)
continue;
if (pt_prev) {
deliver_skb(skb2, pt_prev, skb->dev);
pt_prev = ptype;
continue;
}
}static inline int deliver_skb(struct sk_buff *skb,
struct packet_type *pt_prev,
struct net_device *orig_dev)
{
return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
}
在 dev_queue_xmit_nit 中遍历 ptype_all 中的协议,并依次调用 deliver_skb。这就会执行到 tcpdump 挂在上面的虚拟协议。
总结
- tcpdump 是通过 socket 系统调用,
- 注册packet_rcv回调函数到ptype_all队列中
- 网络收发包,会在网络设备层遍历 ptype_all 中的协议,并执行其中的回调。
- 数据包是先经过网络设备层然后才到协议层n,etfilter在tcpdump收包过程中起不到过滤;在发包过程 IP 层进入各种 netfilter 规则的过滤起作用。
应用层实现抓包关键程序
在应用层实现抓包 类型设置为PF_PACKET
//PF_PACKET
int main(int argc, char *argv[])
{
if( (sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0 ){
printf("Create socket error.\n");
exit(0);
}
while(1){
len = recvfrom(sock, buffer, BUFFER_MAX, 0, NULL, NULL);
if (len < 46) {
printf("Catch packet length error.\n" );
close(sock);
exit(0);
}
}
}
参考
https://course.0voice.com/v1/course/intro?courseId=2&agentId=0
边栏推荐
- 關於ETL看這篇文章就够了,三分鐘讓你明白什麼是ETL
- Jenkins自动化部署,连接不到所依赖的服务【已解决】
- 【NOI模拟赛】摆(线性代数,杜教筛)
- 小程序wx.show
- The pie chart with dimension lines can set various parameter options
- What is graph neural network? Figure what is the use of neural networks?
- every()、map()、forEarch()方法。数组里面有对象的情况
- Rsync for file backup
- 2021-05-20computed和watch应用与区别
- 阿里资深软件测试工程师推荐测试人员必学——安全测试入门介绍
猜你喜欢

opencv最大值滤波(不局限于图像)

Prompt code when MySQL inserts Chinese data due to character set problems: 1366

教程篇(5.0) 08. Fortinet安全架构集成与FortiXDR * FortiEDR * Fortinet 网络安全专家 NSE 5

WebRTC系列-网络传输之5选择最优connection切换

一文详解|增长那些事儿

every()、map()、forEarch()方法。数组里面有对象的情况

开源之夏中选名单已公示,基础软件领域成为今年的热门申请
![[MySQL from introduction to mastery] [advanced part] (I) character set modification and underlying principle](/img/db/e581087e550a2e460f12047685c48f.png)
[MySQL from introduction to mastery] [advanced part] (I) character set modification and underlying principle

A tip to read on Medium for free

What is SRE? A detailed explanation of SRE operation and maintenance system
随机推荐
项目部署相关
The form image uploaded in chorme cannot view the binary image information of the request body
Summary of methods in numpy
微博撰写-流程图-序列图-甘特图-mermaid流程图-效果不错
xargs使用技巧 —— 筑梦之路
Mysql数据(Liunx环境)定时备份
À propos de ETL il suffit de lire cet article, trois minutes pour vous faire comprendre ce qu'est ETL
解析互联网广告术语 CPM、CPC、CPA、CPS、CPL、CPR 是什么意思
Why do you want to file? What are the advantages and disadvantages of website filing?
阿里资深软件测试工程师推荐测试人员必学——安全测试入门介绍
1704. 判断字符串的两半是否相似
IDEA另起一行快捷键
【NOI模拟赛】给国与时光鸡(构造)
什么是图神经网络?图神经网络有什么用?
Jenkins is deployed automatically and cannot connect to the dependent service [solved]
什么是SRE?一文详解SRE运维体系
rpiplay实现树莓派AirPlay投屏器
剑指 Offer 55 - I. 二叉树的深度-dfs法
基于单片机开发的酒精浓度测试仪方案
用VNC Viewer的方式远程连接无需显示屏的树莓派