当前位置:网站首页>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
边栏推荐
- 基于QingCloud的地理信息企业研发云解决方案
- Several schemes of PHP code encryption
- 2020中国全国各省市,三级联动数据,数据机构(数据来自国家统计局官网)
- À propos de ETL il suffit de lire cet article, trois minutes pour vous faire comprendre ce qu'est ETL
- Why can ping fail while traceroute can
- rpiplay实现树莓派AirPlay投屏器
- Using ngrok for intranet penetration
- Why do you want to file? What are the advantages and disadvantages of website filing?
- every()、map()、forEarch()方法。数组里面有对象的情况
- 1528. 重新排列字符串
猜你喜欢

【LeetCode】387. 字符串中的第一个唯一字符

JS to find and update the specified value in the object through the key

Centos7 installation of jdk8, mysql5.7 and Navicat connection to virtual machine MySQL and solutions (solutions to MySQL download errors are attached)

Matlab camera calibrator camera calibration

什么是SRE?一文详解SRE运维体系

The form image uploaded in chorme cannot view the binary image information of the request body

110. 平衡二叉树-递归法

It is enough to read this article about ETL. Three minutes will let you understand what ETL is

Background management of uniapp hot update

数据中台:数据中台技术架构详解
随机推荐
关于ETL看这篇文章就够了,三分钟让你明白什么是ETL
基于QingCloud的地理信息企业研发云解决方案
原生小程序用画布制作海报,等比例缩放,和uniapp差不多就是写法有点不同
数组相向指针系列
Telnet port login method with user name for liunx server
IDEA另起一行快捷键
疫情、失业,2022,我们高喊着摆烂和躺平!
Using skills of xargs -- the way to build a dream
Introduction to data platform
【LeetCode】541. 反转字符串 II
A tip to read on Medium for free
Become an IEEE student member
110. 平衡二叉树-递归法
为什么ping不通,而traceroute却可以通
Detailed explanation of Base64 coding and its variants (to solve the problem that the plus sign changes into a space in the URL)
Earthly container image construction tool -- the road to dream
2022 spring recruitment interview summary
所说的Get post:请求的区别,你真的知道了吗??????
【LeetCode】387. 字符串中的第一个唯一字符
String to Base64